ObjectARX Developers Guide
ObjectARX Developers Guide
IN NO EVENT SHALL AUTODESK, INC. BE LIABLE TO ANYONE FOR SPECIAL, COLLATERAL, INCIDENTAL, OR
CONSEQUENTIAL DAMAGES IN CONNECTION WITH OR ARISING OUT OF PURCHASE OR USE OF THESE MATERIALS. THE
SOLE AND EXCLUSIVE LIABILITY TO AUTODESK, INC., REGARDLESS OF THE FORM OF ACTION, SHALL NOT EXCEED THE
PURCHASE PRICE OF THE MATERIALS DESCRIBED HEREIN.
Autodesk, Inc. reserves the right to revise and improve its products as it sees fit. This publication describes the state of this product
at the time of its publication, and may not reflect the product at all times in the future.
Autodesk Trademarks
The following are registered trademarks of Autodesk, Inc., in the USA and/or other countries: 3D Plan, 3D Props, 3D Studio, 3D
Studio MAX, 3D Studio VIZ, 3D Surfer, ADE, ADI, Advanced Modeling Extension, AEC Authority (logo), AEC-X, AME, Animator
Pro, Animator Studio, ATC, AUGI, AutoCAD, AutoCAD Data Extension, AutoCAD Development System, AutoCAD LT, AutoCAD
Map, Autodesk, Autodesk Animator, Autodesk (logo), Autodesk MapGuide, Autodesk University, Autodesk View, Autodesk
WalkThrough, Autodesk World, AutoLISP, AutoShade, AutoSketch, AutoSolid, AutoSurf, AutoVision, Biped, bringing information
down to earth, CAD Overlay, Character Studio, Design Companion, Drafix, Education by Design, Generic, Generic 3D Drafting,
Generic CADD, Generic Software, Geodyssey, Heidi, HOOPS, Hyperwire, Inside Track, Kinetix, MaterialSpec, Mechanical Desktop,
Multimedia Explorer, NAAUG, Office Series, Opus, PeopleTracker, Physique, Planix, Rastation, Softdesk, Softdesk (logo), Solution
3000, Tech Talk, Texture Universe, The AEC Authority, The Auto Architect, TinkerTech, WHIP!, WHIP! (logo), Woodbourne,
WorkCenter, and World-Creating Toolkit.
The following are trademarks of Autodesk, Inc., in the USA and/or other countries: 3D on the PC, ACAD, ActiveShapes, Actrix,
Advanced User Interface, AEC Office, AME Link, Animation Partner, Animation Player, Animation Pro Player, A Studio in Every
Computer, ATLAST, Auto-Architect, AutoCAD Architectural Desktop, AutoCAD Architectural Desktop Learning Assistance,
AutoCAD DesignCenter, Learning Assistance, AutoCAD LT Learning Assistance, AutoCAD Simulator, AutoCAD SQL Extension,
AutoCAD SQL Interface, AutoCDM, Autodesk Animator Clips, Autodesk Animator Theatre, Autodesk Device Interface, Autodesk
PhotoEDIT, Autodesk Software Developer’s Kit, Autodesk View DwgX, AutoEDM, AutoFlix, AutoLathe, AutoSnap, AutoTrack, Built
with ObjectARX (logo), ClearScale, Concept Studio, Content Explorer, cornerStone Toolkit, Dancing Baby (image), Design Your
World, Design Your World (logo), Designer’s Toolkit, DWG Linking, DWG Unplugged, DXF, Exegis, FLI, FLIC, GDX Driver, Generic
3D, Heads-up Design, Home Series, Kinetix (logo), MAX DWG, ObjectARX, ObjectDBX, Ooga-Chaka, Photo Landscape,
Photoscape, Plugs and Sockets, PolarSnap, Powered with Autodesk Technology, Powered with Autodesk Technology (logo),
ProConnect, ProjectPoint, Pro Landscape, QuickCAD, RadioRay, SchoolBox, SketchTools, Suddenly Everything Clicks,
Supportdesk, The Dancing Baby, Transforms Ideas Into Reality, Visual LISP, and Volo.
Third Party Trademarks
Élan License Manager is a trademark of Élan Computer Group, Inc.
Microsoft, Visual Basic, Visual C++, and Windows are registered trademarks and Visual FoxPro and the Microsoft Visual Basic
Technology logo are trademarks of Microsoft Corporation in the United States and other countries.
All other brand names, product names or trademarks belong to their respective holders.
Third Party Software Program Credits
ACIS ® Copyright © 1994, 1997, 1999 Spatial Technology, Inc., Three-Space Ltd., and Applied Geometry Corp. All rights reserved.
Copyright © 1997 Microsoft Corporation. All rights reserved.
International CorrectSpell™ Spelling Correction System © 1995 by Lernout & Hauspie Speech Products, N.V. All rights reserved.
InstallShield™ 3.0. Copyright © 1997 InstallShield Software Corporation. All rights reserved.
Portions Copyright © 1991-1996 Arthur D. Applegate. All rights reserved.
Portions of this software are based on the work of the Independent JPEG Group.
Typefaces from the Bitstream ® typeface library copyright 1992.
Typefaces from Payne Loving Trust © 1996. All rights reserved.
The license management portion of this product is based on Élan License Manager © 1989, 1990, 1998 Élan Computer Group,
Inc. All rights reserved.
GOVERNMENT USE
Use, duplication, or disclosure by the U. S. Government is subject to restrictions as set forth in FAR 12.212 (Commercial Computer
Software-Restricted Rights) and DFAR 227.7202 (Rights in Technical Data and Computer Software), as applicable.
1 2 3 4 5 6 7 8 9 10
Contents
Chapter 1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
The ObjectARX Programming Environment . . . . . . . . . . . . . . . . . . . . . . . . 8
Accessing the AutoCAD Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Interacting with the AutoCAD Editor . . . . . . . . . . . . . . . . . . . . . . . . . 8
Creating User Interfaces with MFC . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Supporting MDI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Creating Custom Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Building Complex Applications. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Interacting with Other Environments . . . . . . . . . . . . . . . . . . . . . . . . . 9
ObjectARX Class Libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
AcRx Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
AcEd Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
AcDb Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
AcGi Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
AcGe Library. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
iii
System Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Installing ObjectARX. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
iv | Contents
?—List Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Load . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Unload . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Commands. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Options. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Running ObjectARX Applications from AutoLISP . . . . . . . . . . . . . . . . . . . 55
Error Handling. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Contents | v
Chapter 6 Entities. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
Entities Defined . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
Entity Ownership . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
AutoCAD Release 12 Entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
Common Entity Properties. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
Entity Color . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
Entity Linetype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
Entity Linetype Scale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
Entity Visibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
Entity Layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
Common Entity Functions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
Object Snap Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
Transform Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
Intersecting for Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
GS Markers and Subentities. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
Exploding Entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
Creating Instances of AutoCAD Entities . . . . . . . . . . . . . . . . . . . . . . . . . 125
Creating a Simple Entity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
Creating a Simple Block Table Record . . . . . . . . . . . . . . . . . . . . . . 126
Creating a Block Table Record with Attribute Definitions . . . . . . . 126
Creating a Block Reference with Attributes . . . . . . . . . . . . . . . . . . 129
Iterating through a Block Table Record . . . . . . . . . . . . . . . . . . . . . 133
Complex Entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
Creating a Complex Entity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
Iterating through Vertices in a Polyline . . . . . . . . . . . . . . . . . . . . . 135
Coordinate System Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
Entity Coordinate System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
AcDb2dPolylineVertex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
Curve Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
Associating Hyperlinks with Entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
AcDbHyperlink Class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
AcDbHyperlinkCollection Class . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
AcDbEntityHyperlinkPE Class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
Hyperlink Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
vi | Contents
Layout Dictionary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
Creating a Dictionary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
Iterating over Dictionary Entries . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
Layouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
ObjectARX Layout Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
Xrecords. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
DXF Group Codes for Xrecords . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
Contents | vii
Chapter 9 Selection Set, Entity, and Symbol Table Functions . . . . . . . . . . . 199
Selection Set and Entity Names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
Handling Selection Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
Selection Set Filter Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
Selection Set Manipulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
Transformation of Selection Sets. . . . . . . . . . . . . . . . . . . . . . . . . . . 211
Entity Name and Data Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
Entity Name Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
Entity Data Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Entity Data Functions and Graphics Screen . . . . . . . . . . . . . . . . . . 233
Notes on Extended Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
Xrecord Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
Symbol Table Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
viii | Contents
Class Initialization Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
Contents | ix
Chapter 13 Deriving from AcDbEntity. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
Deriving Custom Entities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
AcDbEntity Functions to Override . . . . . . . . . . . . . . . . . . . . . . . . . 350
AcDbEntity Functions Usually Overridden. . . . . . . . . . . . . . . . . . . 351
AcDbEntity Functions Rarely Overridden. . . . . . . . . . . . . . . . . . . . 352
Overriding Common Entity Functions . . . . . . . . . . . . . . . . . . . . . . . . . . 353
Overriding worldDraw() and viewportDraw() . . . . . . . . . . . . . . . . 353
Overriding saveAs() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
Implementing the Object Snap Point Function . . . . . . . . . . . . . . . 357
Implementing the Grip Point Functions . . . . . . . . . . . . . . . . . . . . 359
Implementing the Stretch Point Functions . . . . . . . . . . . . . . . . . . 361
Transformation Functions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
Intersecting with Other Entities . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
Intersecting a Custom Entity with Another Entity. . . . . . . . . . . . . 369
Exploding an Entity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370
Extending Entity Functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370
Using AcEdJig . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
Deriving a New Class from AcEdJig . . . . . . . . . . . . . . . . . . . . . . . . 371
General Steps for Using AcEdJig . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
Setting Up Parameters for the Drag Sequence . . . . . . . . . . . . . . . . 372
Drag Loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
Implementing the sampler(), update(), and entity() Functions . . 375
Adding the Entity to the Database . . . . . . . . . . . . . . . . . . . . . . . . . 378
Sample Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
x | Contents
Using an Editor Reactor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
Using a Database Reactor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
Using an Object Reactor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402
Notification Use Guidelines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412
Contents | xi
Nonreentrant Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431
Making a Command Nonreentrant . . . . . . . . . . . . . . . . . . . . . . . . 432
Nonreentrant AutoCAD Commands . . . . . . . . . . . . . . . . . . . . . . . 432
Multi-Document Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 432
Disabling Document Switching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435
Application Execution Context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436
Code Invoked under the Application Execution Context . . . . . . . 436
Code Differences under the Application Execution Context . . . . . 436
Other Application Execution Context Considerations. . . . . . . . . . 437
Database Undo and Transaction Management Facilities. . . . . . . . . . . . . 438
Document-Independent Databases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439
An MDI-Aware Example Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
xii | Contents
Insert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 504
Editor Reactor Notification Functions . . . . . . . . . . . . . . . . . . . . . . . 504
Contents | xiii
Input Context Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 569
Input Point Filters and Monitors . . . . . . . . . . . . . . . . . . . . . . . . . . 575
Chapter 23 COM, ActiveX Automation, and the Object Property Manager 593
Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 594
Using AutoCAD COM Objects from ObjectARX and Other
Environments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 594
Accessing COM Interfaces from ObjectARX . . . . . . . . . . . . . . . . . . 595
AutoCAD ActiveX Automation Implementation. . . . . . . . . . . . . . . . . . . 605
The Relationship between AcDbObjects and Automation Objects 605
Creating the COM Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 608
Interacting with AutoCAD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 611
Document Locking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 612
Creating a Registry File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 613
Exposing Automation Functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615
Setting Up an ATL Project File . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615
Writing a COM Wrapper. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616
Building and Registering a COM DLL . . . . . . . . . . . . . . . . . . . . . . . 621
Object Property Manager API . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 622
AutoCAD COM Implementation . . . . . . . . . . . . . . . . . . . . . . . . . . 623
Static OPM COM Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 624
ICategorizeProperties Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . 624
IPerPropertyBrowsing Interface. . . . . . . . . . . . . . . . . . . . . . . . . . . . 624
IOPMPropertyExtension Interface . . . . . . . . . . . . . . . . . . . . . . . . . 625
IOPMPropertyExpander Interface . . . . . . . . . . . . . . . . . . . . . . . . . . 625
Implementing Static OPM Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625
Dynamic Properties and OPM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 630
IDynamicProperty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 631
xiv | Contents
IAcDcContentFinderSite Interface . . . . . . . . . . . . . . . . . . . . . . . . . . 634
IAcDcContentFinder Interface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635
IAcPostDrop Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635
Registry Requirements for an AutoCAD DesignCenter Component . . . . 635
Applications Key . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 635
Extensions Key . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636
CLASSID Registration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 637
Implementing the Interfaces for AutoCAD DesignCenter . . . . . . . . . . . . 638
Customizing AutoCAD DesignCenter. . . . . . . . . . . . . . . . . . . . . . . . . . . . 640
Create an ActiveX Template Library Project . . . . . . . . . . . . . . . . . . 641
Add Registry Support and a New ATL COM Object . . . . . . . . . . . . 641
Add Code to Support the New ATL COM Object . . . . . . . . . . . . . . 644
Contents | xv
Installing the ObjectDBX Libraries. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672
Use COMMONFILES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 672
Install by Version and as SHAREDFILE . . . . . . . . . . . . . . . . . . . . . . 673
Ensure the Files Are on the Path . . . . . . . . . . . . . . . . . . . . . . . . . . . 673
Ensure Smart Pathing Updates . . . . . . . . . . . . . . . . . . . . . . . . . . . . 674
Tips and Techniques. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676
ACAD_OBJID_INLINE_INTERNAL . . . . . . . . . . . . . . . . . . . . . . . . . 676
AcDbDatabase Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676
AcDbDatabase::insert() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 678
Finding the Active Viewports in Model Space . . . . . . . . . . . . . . . . 678
Details About Viewports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 679
Always Test Your Drawings in AutoCAD 2000 . . . . . . . . . . . . . . . . 680
Using DWG Files from Earlier Releases . . . . . . . . . . . . . . . . . . . . . . 680
Extended Entity Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681
Raster Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682
Known Limitations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682
xvi | Contents
Clip Boundary Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 724
Contents | xvii
Part VII Appendixes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 767
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 809
xviii | Contents
About ObjectARX
Documentation
In This Chapter
1
The ObjectARX Documentation Set
The ObjectARX documentation set includes the following printed guides and
online documentation.
Printed Guides
Two printed manuals are provided with ObjectARX:
Online Documentation
You can access the ObjectARX online documentation from the
\objectarx\docs directory. The following online documents are provided in
Windows help file format:
NOTE The logo program guidelines contain information about how to regis-
ter your Registered Developer Symbol (RDS) with Autodesk, in addition to the
other logo requirements.
Where to Start
New users should start with the ObjectARX Developer’s Guide. Experienced
users and those upgrading from previous versions of ObjectARX should start
with the Migration Guide for Applications, and then move on to the more
detailed material on the new subjects in the ObjectARX Developer’s Guide.
Organization
The ObjectARX Developer’s Guide is organized in seven parts:
Part I: Using ObjectARX describes the fundamental concepts of ObjectARX.
Part II: User Interfaces shows how to work with ObjectARX global functions
and MFC to create and interact with user interfaces.
Part III: Defining New Classes describes how to create custom classes in
ObjectARX.
Part IV: Specialized Topics examines topics of interest to more advanced users,
such as proxy objects, notification, and protocol extension.
Part V: Interacting with Other Environments discusses working with external
programming environments such as COM and ActiveX® Automation.
Part VI: ObjectARX Libraries describes several of the ObjectARX libraries,
including the ObjectDBX™ libraries and the graphics interface library.
Part VII: Appendixes gives detailed information about migrating ADS pro-
grams to ObjectARX, and using programmable dialog boxes.
5
6
Overview
In This Chapter
1
An ObjectARX application is a dynamic link library ■ The ObjectARX Programming
Environment
(DLL) that shares the address space of AutoCAD and
■ ObjectARX Class Libraries
makes direct function calls to AutoCAD. You can add ■ Getting Started
7
The ObjectARX Programming Environment
The ObjectARX programming environment provides an object-oriented C++
application programming interface for developers to use, customize, and
extend AutoCAD. The ObjectARX libraries comprise a versatile set of tools for
application developers to take advantage of AutoCAD’s open architecture,
providing direct access to AutoCAD database structures, the graphics system,
and native command definition. In addition, these libraries are designed to
work in conjunction with Visual LISP and other application programming
interfaces so that developers can choose the programming tools best suited
to their needs and experience.
As a developer, you can use ObjectARX to accomplish the following tasks:
The next sections take a brief look at these topics. They will be discussed in
greater detail throughout the book.
8 | Chapter 1 Overview
Creating User Interfaces with MFC
ObjectARX applications can be built with a dynamically linked MFC library
that is shared with AutoCAD. You can use this library to create standard
Microsoft Windows graphical user interfaces (GUIs).
Supporting MDI
With ObjectARX, you can create applications that will support the AutoCAD
multiple document interface, and you can ensure that your applications
will interact properly with other applications in the Microsoft Windows
environment.
■ Notification
■ Transaction management
■ Deep cloning
■ Reference editing
■ Protocol extension
■ Proxy object support
The following sections take a closer look at each of the ObjectARX libraries.
For more information about specific classes and member functions, see the
ObjectARX Reference.
AcRx Library
The AcRx library provides system-level classes for DLL initialization and link-
ing and for runtime class registration and identification. The base class of this
library is AcRxObject, which provides the following facilities:
10 | Chapter 1 Overview
■ Object runtime class identification and inheritance analysis
■ Runtime addition of new protocol to an existing class (see chapter 19,
“Protocol Extension”)
■ Object equality and comparison testing
■ Object copy
The AcRx library also provides a set of C++ macros to help you create new
ObjectARX classes derived from AcRxObject (see chapter 11, “Deriving a
Custom ObjectARX Class”).
AcRxDictionary is another important class in this library. A dictionary is a
mapping from a text string to another object. The AcRx library places its
objects, classes, and service dictionaries in a global object dictionary, which
is an instance of the AcRxDictionary class. Applications can add objects to
this dictionary so that they are accessible to other applications.
The class hierarchy for the AcRx library is as follows:
AcRxClass
AcRxDictionary
AcRxDynamicLinker
AcRxEvent
AcEditor
AcRxService
AcRxKernal
AcDbServices
AcEdServices
AcadAppInfo
AcDb Library
The AcDb library provides the classes that compose the AutoCAD database.
This database stores all the information for the graphical objects, called
entities, that compose an AutoCAD drawing, as well as the nongraphical
objects (for example, layers, linetypes, and text styles) that are also part of a
drawing. You can query and manipulate existing instances of AutoCAD
entities and objects with the AcDb library, and you can create new instances
of database objects.
The AutoCAD database contains these major elements:
■ A set of nine symbol tables that own uniquely named symbol table entry
objects. These objects represent various commonly used AcDbDatabase
objects and data members.
12 | Chapter 1 Overview
■ A named object dictionary (of class AcDbDictionary), which provides the
“table of contents” for an AutoCAD drawing. Initially, this table of con-
tents contains the IDs of the four other dictionaries used by AutoCAD.
Applications you develop, however, are free to add other objects to the
dictionary.
■ A fixed set of about 200 header variables, whose values are set by
AutoCAD.
For more information on the AcDb library, see chapter 2, “Database Primer,”
chapter 4, “Database Operations,” chapter 5, “Database Objects,” chapter 6,
“Entities,” and chapter 7, “Container Objects.” For information on deriving
new classes from AcDbObject and AcDbEntity, see chapter 12, “Deriving from
AcDbObject” and chapter 13, “Deriving from AcDbEntity.”
AcGi Library
The AcGi library provides the graphics interface used for drawing AutoCAD
entities. This library is used by the AcDbEntity member functions
worldDraw(), viewportDraw(), and saveAs(), all part of the standard entity
protocol. The worldDraw() function must be defined by all custom entity
classes. The AcGiWorldDraw object provides an API through which
AcDbEntity::worldDraw() can produce its graphical representation in all
viewports simultaneously. Similarly, the AcGiViewportDraw object provides
an API through which the AcDbEntity::viewportDraw() function can pro-
duce different graphical representations for each viewport.
AcGiCommonDraw
AcGiWorldDraw
AcGiWorldDraw
AcGiContext
AcGiEdgeData
AcGiFaceData
AcGiGeometry
AcGiViewportGeometry
AcGiWorldGeometry
AcGiLinetypeEngine
AcGiSubEntityTraits
AcGiDrawableTraits
AcGiTextStyle
AcGiVertexData
AcGiViewport
AcGiDrawable
AcGiGlyph
For more information on using AcGi classes, see chapter 13, “Deriving from
AcDbEntity.”
AcGe Library
The AcGe library is used by the AcDb library and provides utility classes such
as vectors and matrices that are used to perform common 2D and 3D geomet-
ric operations. It also provides basic geometric objects such as points, curves,
and surfaces.
The AcGe library consists of two major subsets: classes for 2D geometry and
classes for 3D geometry. The major abstract base classes are AcGeEntity2d
and AcGeEntity3d. Several basic classes not derived from any other class
include AcGePoint2d, AcGeVector2d, and AcGeMatrix2d (shown at the begin-
ning of the class hierarchy). These basic classes can be used to perform many
types of common operations, such as adding a vector to a point, computing
the dot or cross product of two vectors, and computing the product of two
matrices. The higher-level classes of this library are implemented using these
basic classes. The class hierarchy for the AcGe library is as follows:
14 | Chapter 1 Overview
AcGeBoundBlock2d AcGeBoundBlock3d
AcGeClipBoundary2d AcGeCurve3d
AcGeCurve2d AcGeCircArc3de
AcGeCircArc2d AcGeCompositeCurve3d
AcGeCompositeCurve2d AcGeEllipArc3e
AcGeEllipArc2d AcGeExternalCurve3d
AcGeExternalCurve2d AcGeLinearEnt3d
AcGeLinearEnt2d AcGeLine3d
AcGeLine2d AcGeLineSeg3d
AcGeLineSeg2d AcGeRay3d
AcGeRay2d AcGeMatrix3d
AcGeOffsetCurve2d AcGeOffsetCurve3d
AcGeSplineEnt2d AcGeSplineEnt3d
AcGeCubicSplineCurve2d AcGeCubicSplineCurve3d
AcGeNurbCurve2d AcGeNurbCurve3d
AcGePolyline2d AcGePolyline3d
AcGeCurveCurveInt2d AcGeAugPolyline3d
AcGePointEnt2d AcGeCurveCurveInt3d
AcGePointOnCurve2d AcGeCurveSurfInt
AcGePosition2d AcGePointEnt3d
AcGePointOnCurve3d
AcGePointOnSurface
AcGeCurveBoundary
AcGePosition3d
AcGe
AcGeSurfSurfInt
AcGeContext
AcGeSurface
AcGeDwgIO
AcGeCone
AcGeDxfIO
AcGeCylinder
AcGeFileIO
AcGeExternalBoundedSurface
AcGeFiler
AcGeExternalSurface
AcGeInterval
AcGeNurbSurface
AcGeKnotVector
AcGeOffsetSurface
AcGeLibVersion
AcGePlanarEnt
AcGeMatrix2d
AcGeBoundedPlanet
AcGeMatrix3d
AcGePlane
AcGePoint2d
AcGeSphere
AcAxPoint2d
AcGeTorus
AcGePoint3d
AcAxPoint3d
AcGeScale2d
AcGeScale3d
AcGeTol
AcGeVector2d
AcGeVector3d
The AcGe library provides several different coordinate systems. For more
information, see chapter 27, “Using the Geometry Library.” The sample pro-
grams in this manual illustrate numerous common uses of AcGe classes.
System Requirements
Developing applications with ObjectARX requires the following software and
hardware:
Installing ObjectARX
When you install ObjectARX, a setup program guides you through the
process.
To install ObjectARX
1 Insert the CD into the CD-ROM drive.
2 If you are running Windows NT 4.0 with AutoPlay, follow the on-screen
instructions.
3 If you have turned off AutoPlay in Windows NT 4.0, from the Start menu on
the taskbar, choose Run, designate the CD-ROM drive, enter the path name,
and then enter setup.
16 | Chapter 1 Overview
classmap The classmap directory contains an AutoCAD drawing
illustrating the ObjectARX class hierarchy.
docs The docs directory contains Windows online help files for
ObjectARX developers, including the ObjectARX
Developer’s Guide, the ObjectARX Reference, the Migration
Guide for Applications, and the ObjectARX Readme file.
docsamps The docsamps directory contains subdirectories for each of
the programs from which examples were extracted for the
ObjectARX Developer’s Guide. Each subdirectory contains
the full set of source code for the application and an
explanatory Readme file.
inc The inc directory contains the ObjectARX header files.
lib The lib directory contains the ObjectARX library files.
redistrib The redistrib directory contains a set of DLLs, some of
which may be required for an ObjectARX application to
run. Developers should copy the DLLs that they need for
application development to a directory in the AutoCAD
search path, and package the necessary DLLs with their
ObjectARX applications for distribution.
samples The samples directory includes subdirectories containing
examples of ObjectARX applications. These subdirectories
include source code and Readme files. The most significant
set of sample ObjectARX applications is in the polysamp
subdirectory.
utils The utils directory contains subdirectories for applications
that are extensions to ObjectARX, including brep for
boundary representation and istorage for compound
document storage. Each application directory includes
inc, lib, and sample subdirectories.
Getting Started | 17
18
Database Primer
In This Chapter
2
The AutoCAD database stores the objects and entities ■ AutoCAD Database Overview
■ Essential Database Objects
that make up an AutoCAD drawing. This chapter
■ Creating Objects in AutoCAD
discusses the key elements of the database: entities, ■ Creating Objects in ObjectARX
the database.
19
AutoCAD Database Overview
An AutoCAD drawing is a collection of objects stored in a database. Some of
the basic database objects are entities, symbol tables, and dictionaries. Enti-
ties are a special kind of database object that have a graphical representation
within an AutoCAD drawing. Lines, circles, arcs, text, solids, regions, splines,
and ellipses are examples of entities. A user can see an entity on the screen
and can manipulate it.
Symbol tables and dictionaries are containers used to store database objects.
Both container objects map a symbol name (a text string) to a database
object. An AutoCAD database includes a fixed set of symbol tables, each of
which contains instances of a particular class of symbol table record. You
cannot add a new symbol table to the database. Examples of symbol tables
are the layer table (AcDbLayerTable), which contains layer table records, and
the block table (AcDbBlockTable), which contains block table records. All
AutoCAD entities are owned by block table records.
Dictionaries provide a more generic container for storing objects than sym-
bol tables. A dictionary can contain any object of the type AcDbObject or sub-
class thereof. The AutoCAD database creates a dictionary called the named
object dictionary when it creates a new drawing. The named object
dictionary can be viewed as the master “table of contents” for all of the dic-
tionaries associated with the database. You can create new dictionaries
within the named object dictionary and add new database objects to them.
The following figure shows the key components of the AutoCAD database.
Database
Objects
Their Symbol
Layer Table Record Block Table Record
Table Records
Entity
Multiple Databases
Multiple databases can be loaded in a single AutoCAD session. Each object in
the session has a handle and an object ID. A handle uniquely identifies the
object within the scope of a particular database, whereas an object ID
uniquely identifies the object across all databases loaded at one time. An
object ID only persists during an edit session, but a handle gets saved with
the drawing. In contrast to the object ID, an object handle is not guaranteed
to be unique when multiple databases are loaded in an AutoCAD session.
■ Create an object and append it to the database. The database then gives
the object an ID and returns it to you.
■ Use the database protocol for obtaining the object ID of the objects that
are created automatically when a database is created (such as the fixed set
of symbol tables and the named object dictionary).
■ Use class-specific protocol for obtaining object IDs. Certain classes, such
as symbol tables and dictionaries, define objects that own other objects.
These classes provide protocol for obtaining the object IDs of the owned
objects.
■ Use an iterator to step through a list or set of objects. The AcDb library
provides a number of iterators that can be used to step through various
kinds of container objects (AcDbDictionaryIterator,
AcDbObjectIterator).
■ Query a selection set. After the user has selected an object, you can ask the
selection set for the list of entity names of the selected objects, and from
the names convert to the object IDs. For more information on selection
sets, see chapter 6, “Entities.”
■ A set of nine symbol tables that includes the block table, layer table, and
linetype table. The block table initially contains three records: a record
called *MODEL_SPACE, and two paper space records called *PAPER_SPACE
and *PAPER_SPACE0. These block table records represent model space and
the two predefined paper space layouts. The layer table initially contains
one record, layer 0. The linetype table initially contains the
CONTINUOUS linetype.
■ A named object dictionary. When a database is created, this dictionary
already contains four database dictionaries: the GROUP dictionary, MLINE
style dictionary, layout dictionary, and plot style name dictionary. Within
the MLINE style dictionary, the STANDARD style is always present.
Paper Space
Block Table
Model Space
Line
When you first invoke AutoCAD and the database is in its default state, enti-
ties are added to model space, the main space in AutoCAD, which is used for
model geometry and graphics. Paper space is intended to support “documen-
tation” geometry and graphics, such as drafting sheet outlines, title blocks,
and annotational text. The entity creation commands in AutoCAD ( LINE, in
this case) cause the entity to be added to the current database as well as to
the model space block. You can ask any entity which database and which
block it belongs to.
Next, suppose the user creates a circle with this command:
circle 9,3 2
Paper Space
Block Table
Model Space
Line
Circle
AutoCAD creates a new layer table record to hold the layer and then adds it
to the layer table.
Paper Space
Block Table
Model Space
Line
Circle
Layer Table
layer 0
my layer
AutoCAD creates a new group object and adds it to the GROUP dictionary,
which is contained in the named object dictionary. The new group contains
a list of the object IDs of the objects that compose the group.
Group
Dictionary New Group
Named Object
Dictionary
MLINE Style
Dictionary
Creating Entities
The following ObjectARX code creates the line and adds it to the model space
block table record:
AcDbObjectId
createLine()
{
AcGePoint3d startPt(4.0, 2.0, 0.0);
AcGePoint3d endPt(10.0, 7.0, 0.0);
AcDbLine *pLine = new AcDbLine(startPt, endPt);
AcDbBlockTable *pBlockTable;
acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pBlockTable, AcDb::kForRead);
AcDbBlockTableRecord *pBlockTableRecord;
pBlockTable->getAt(ACDB_MODEL_SPACE, pBlockTableRecord,
AcDb::kForWrite);
pBlockTable->close();
AcDbObjectId lineId;
pBlockTableRecord->appendAcDbEntity(lineId, pLine);
pBlockTableRecord->close();
pLine->close();
return lineId;
}
The createLine() routine obtains the block table for the current drawing.
Then it opens the model space block table record for writing. After closing
the block table, it adds the entity to the block table record and then closes
the block table record and the entity.
NOTE When you are done using any ObjectARX objects, you must explicitly
close them as soon as possible.
AcDbBlockTable *pBlockTable;
acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pBlockTable, AcDb::kForRead);
AcDbBlockTableRecord *pBlockTableRecord;
pBlockTable->getAt(ACDB_MODEL_SPACE, pBlockTableRecord,
AcDb::kForWrite);
pBlockTable->close();
AcDbObjectId circleId;
pBlockTableRecord->appendAcDbEntity(circleId, pCirc);
pBlockTableRecord->close();
pCirc->close();
return circleId;
}
AcDbLayerTableRecord *pLayerTableRecord =
new AcDbLayerTableRecord;
pLayerTableRecord->setName("ASDK_MYLAYER");
pEntity->setColorIndex(newColor);
pEntity->close();
return Acad::eOk;
}
New instances of an object are considered to be open for write. Some func-
tions, such as the AcDbBlockTable::getAt() function, obtain an object ID
and open the object at the same time. An object can’t be closed until it has
been added to the database. You own the object and can freely delete it at any
time before the object is added to the database.
However, once the object has been added to the database, you cannot delete
it directly. You can call the AcDbObject::erase() function, which marks the
object as erased. Erased objects remain in the database until the database is
destroyed, but do not get saved when the drawing is saved.
WARNING! Directly deleting an object that has been added to the database
will cause AutoCAD to terminate.
AcDbObjectId pGroupId;
pGroupDict->setAt(pGroupName, pGroup, pGroupId);
pGroupDict->close();
pGroup->close();
}
In This Chapter
3
This chapter describes how to write and run an ■ Creating an ObjectARX
Application
ObjectARX application. It lists the messages passed by
■ Example Application
AutoCAD to the ObjectARX application and shows how ■ Registering New Commands
■ Loading an ObjectARX
the application typically responds to those Application
29
Creating an ObjectARX Application
An ObjectARX application is a DLL that shares AutoCAD’s address space and
makes direct function calls to AutoCAD. ObjectARX applications typically
implement commands that can be accessed from within AutoCAD. These
commands are often implemented using custom classes. Creating an
ObjectARX application involves the following general steps.
The following five tables describe the messages that AutoCAD sends to
ObjectARX applications. The first table lists messages sent to all applications.
Message Description
kLoadDwgMsg Sent once when the drawing is opened. Then, if the application
registers any functions with AutoLISP, AutoCAD sends this
message once for each drawing loaded into the editor. The
AutoCAD editor is fully initialized at this point, and all global
functions are available. However, you cannot use an
acedCommand() function from a kLoadDwgMsg.
kPreQuitMsg Sent when AutoCAD quits, but before it begins to unload all
ObjectARX applications.
Message Description
kEndMsg Sent only when the END command is entered and there are
changes that need to be saved (when dbmod != 0). kEndMsg is
not sent for a NEW or OPEN, instead, kSaveMsg and
kLoadDwgMsg are sent. For END, if dbmod = 0, then kQuitMsg
is sent instead of kEndMsg.
kQuitMsg Sent when AutoCAD quits (ends without saving) the drawing
because a QUIT command was entered. The kQuitMsg can also
be received with the END command, as noted above. If the END
command is sent and dbmod = 0, then kQuitMsg is sent.
The next table lists the messages that an application receives if it has
registered a service with ObjectARX.
Message Description
Message Description
See the rxdefs.h file where these enumeration constants are defined by the
AppMsgCode type declaration.
You will need to decide which messages your ObjectARX application will
respond to. The following table describes recommended actions upon receipt
of a given message.
Don’t release system resources that are not tied to an edit session,
or clean up AcRx classes, AcEd reactors, or commands; they remain
valid across edit sessions.
kDependencyMsg Do perform any actions that are necessary for your application
when other applications become dependent on it, such as locking
your application so that it cannot be unloaded.
kNoDependencyMsg Do perform any actions that are necessary for your application
when there are no longer any other applications dependent on
yours, such as unlocking your application so that it can be
unloaded by the user if desired.
Time
Quit kUnloadDwgMsg
kQuit
kUnloadApp
WARNING! Using kRetError for the final return value from the
acrxEntryPoint() function will cause your application to be unloaded, except
for the messages kOleUnloadAppMsg and kUnloadAppMsg. In these cases, if
kRetError is returned, the application will not be unloaded.
acedRegCmds->addCommand("ASDK_DICTIONARY_COMMANDS",
"ASDK_ITERATE", "ITERATE", ACRX_CMD_MODAL,
iterateDictionary);
AsdkMyClass::rxInit();
acrxBuildClassHierarchy();
}
Example Application | 39
Registering New Commands
This section describes adding new commands using the AcEd command
registration mechanism. For information on adding new commands using
the functions acedDefun() and acedRegFunc(), see chapter 20, “ObjectARX
Global Utility Functions.” For information on adding new commands using
the AutoLISP defun function, see the AutoCAD Customization Guide.
Command Stack
AutoCAD commands are stored in groups in the command stack, which is
defined by the AcEdCommandStack class. One instance of the command stack
is created per AutoCAD session. This stack consists of the custom commands
that you have defined. The acedRegCmds() macro gives you access to the
command stack.
When you add a command, you also assign it a group name. A good policy
is to use your registered developer prefix for the group name to avoid name
collisions with other commands. Command names within a given group
must be unique, and group names must be unique. However, multiple appli-
cations can add a command of the same name, because the group name
makes the commands unambiguous.
Lookup Order
When a command is invoked, the command stack is searched by group name,
then by command name within the group. In general, the first group registered
will be the first one searched, but you cannot always predict what this order
will be. Use the AcEdCommandStack::popGroupToTop() function to specify that
a particular group should be searched first. At the user level, the Group option
of the ARX command allows the user to specify which group to search first.
Unlocking Applications
By default, applications are locked and cannot be unloaded. To be classified
as an “unloadable” application, the application must ensure that AutoCAD
and other applications no longer refer to any objects or structures the appli-
cation has defined. Before you make an application unloadable, be very
careful that no client applications contain active pointers to any objects in
your address space. For the list of cleanup operations an application must
perform to be unloadable, see “Preparing for Unloading” on page 38.
If you want to make your application unloadable, you need to store the value
of the pkt parameter sent with the AcRx::kInitAppMsg. The pkt parameter
will be used by the unlockApplication() function. By default, an application
is locked. If you unlock an application, it can be unloaded.
Use the following two functions to lock and unlock an application:
bool
AcRxDynamicLinker::lockApplication(void* pkt) const;
bool
AcRxDynamicLinker::unlockApplication(void* pkt) const;
The following function checks whether or not an application is locked:
bool
AcRxDynamicLinker::isApplicationLocked(const char* name)
const;
Analogous global functions are also provided:
bool
acrxLockApplication(void* pkt);
bool
acrxApplicationIsLocked(const char* modulename);
Demand Loading
Demand loading is a feature of AutoCAD that automatically attempts to load
an ObjectARX application that is not resident in AutoCAD. ObjectARX appli-
cations can be designed for loading by AutoCAD under one or more of the
following circumstances:
■ When a drawing file that contains custom objects created by the absent
application is read
■ When a user or another application issues one of the absent application’s
commands
■ When AutoCAD is started
■ Limits the creation of proxy objects (see chapter 14, “Proxy Objects”)
■ Provides greater flexibility for loading ObjectARX applications
■ Conserves memory by loading applications only when their functionality
is required
Demand Loading | 45
AutoCAD, the Windows System Registry, and
ObjectARX Applications
AutoCAD uses the Windows system registry to maintain a wide range of
application information, including information that uniquely identifies dif-
ferent AutoCAD releases, language versions, and products (such as AutoCAD
Map®) that may be installed on any given computer. The registry informa-
tion that identifies different versions of AutoCAD is of particular significance
for ObjectARX developers. The installation program for an ObjectARX appli-
cation must associate information about that ObjectARX application with
information about the version(s) of AutoCAD with which it is supposed to
run.
The AutoCAD installation program creates a unique time stamp key in the
system registry immediately below the release number key (as well as adding
the same installation ID to the executable itself). This key ensures that differ-
ent versions of AutoCAD from the same release will be able to populate their
own sections of the system registry. Within this key, values are stored for the
location of AutoCAD files, the language version, and the product name, as
illustrated in this example:
\\HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R15.0\
ACAD-1:409\
...
AcadLocation:REG_SZ:f:\ACAD2000
Language:REG_SZ:English
ProductName:REG_SZ:AutoCAD Map R15.0
...
The installation program for an ObjectARX application must be able to locate
the appropriate AutoCAD release key, as well as the appropriate language and
product values.
The time stamp key is also used to identify the version of AutoCAD that is
currently loaded (or the version that was most recently loaded). This identi-
fication is necessary, because the “current” version of AutoCAD resets the
information in the global HKEY_CLASSES_ROOT section of the registry for its
own use when it is loaded.
The CurVer value in the release key section of the registry is used to identify
the current version, for example:
\\HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\R15.0\
...
CurVer:REG_SZ:ACAD-1:409
When AutoCAD attempts to demand load an ObjectARX application, it looks
in the section of the registry that belongs to the latest release of AutoCAD for
information about the ObjectARX application. If it does not find the
■ Verification that the sections of the system registry for the appropriate
version of AutoCAD exist. (If the AutoCAD section of the registry does not
exist, the user should be warned that a compatible version of AutoCAD
has not been installed, and the installation should be aborted.)
■ Creation of a specific set of keys and values for the application within the
section(s) of the system registry for the appropriate version(s) of
AutoCAD.
■ Creation of a major key for the application itself, and population of that
key with another set of specific keys and values.
Demand Loading | 47
Creating AutoCAD Subkeys and Values
The ObjectARX application’s installation program must be designed to man-
age a set of keys and values for that application within the section of system
registry for each version of AutoCAD with which it is intended to run. The
following example shows the layout of the keys and values in the section of
the registry that must be created and maintained for the application:
\\HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD\releaseNum\
ACAD-1:LocaleID\
Applications\
ApplicationName\
LoadCtrls:REG_DWORD:acrxAppLoadReason
RegPath:REG_SZ:RegistryPathWhereLoaderIsSpecified
The releaseNum and ACAD-1:LocaleID keys are created by the AutoCAD
installation program.
The ApplicationName key must be the logical name of the application, which
is used internally by AutoCAD to identify the program.
The acrxAppLoadReason value defines the conditions under which the appli-
cation will be loaded, using one or more logical ORs of the following hex
values listed with their associated meanings:
0x01 Load the application upon detection of proxy object.
0x02 Load the application upon AutoCAD startup.
0x04 Load the application upon invocation of a command.
0x08 Load the application upon request by the user or another
application.
0x10 Do not load the application.
The RegistryPathWhereLoaderIsSpecified value must identify the registry
path for the application’s own section of the registry.
The ObjectARX API includes the acrxRegisterApp() function, which may be
used in an ObjectARX application to enter information about the application
into the AutoCAD section of the system registry. Typically,
acrxRegisterApp() would enter this information the first time the applica-
tion is loaded, and confirm the presence of that information on subsequent
loads.
Demand Loading | 49
The legitimate values for the system variable may be used in combination.
They are defined as follows:
0 Disables demand loading of all ObjectARX applications.
1 Enables demand loading of ObjectARX applications upon
detection of proxy objects.
2 Enables demand loading of ObjectARX applications upon
command invocation.
3 Enables demand loading for both proxy objects and
command invocation (the default).
The DEMANDLOAD system variable allows the user to disable demand loading
of all ObjectARX applications that have system registry settings specifying
demand loading on command invocation, and proxy detection. It cannot
cause an application to be demand loaded if the appropriate system registry
settings do not exist.
NOTE Demand loading on detection of custom classes will only work with
classes that are derived from AcDbObject, either directly or indirectly.
Demand Loading | 51
In this example, the developer’s registered developer prefix (ASDK) is used as
the prefix for all commands to ensure that there will be no possible conflict
with commands of the same name in other applications.
The ObjectARX application must also include the appropriate calls to the
acedRegCmds macro for demand loading on command to work.
?—List Applications
Lists the currently loaded ARX applications.
Load
Loads the .arx file that you specify in the standard file dialog box. If FILEDIA
is set to 0, a dialog box is not displayed, and you enter the name of the file
to load in response to the following prompt:
Runtime extension file: Enter a name
Unload
Unloads the specified ARX program. Some applications cannot be unloaded.
See “Unloading an ObjectARX Application” on page 44 for a description of
how the programmer decides whether a program can be unloaded by the user
with this command.
Commands
Displays all command names in all command groups registered from ARX
programs.
Options
Presents developer-related ARX application options.
Options (Group/CLasses/Services): Enter an option
■ Group
Moves the specified group of commands registered from ARX applications
to be the first group searched when resolving the names of AutoCAD
commands. Other registered groups, if there are any, are subsequently
searched, in the same order as before the ARX command was executed.
Command Group Name: Enter the command group name
ARX Command | 53
The search order is important only when a command name is listed in
multiple groups. This mechanism allows different ARX applications to
define the same command names in their own separate command groups.
ARX applications that define command groups should publish the group
name in their documentation.
Group is not intended to be selected by the user directly. The user specifies
which group is searched first by interacting with a script that executes the
ARX command with the Group option. This capability is usually embed-
ded in key menu item scripts. The user selects a menu item from the script.
The key menu item script executes the Group option to establish which
group is searched first, giving commands of the same name (but probably
different functionality) from one application precedence over commands
from another.
For example, applications called ABC Construction and XYZ Interiors
define command groups ABC and XYZ, respectively. Most of ABC Con-
struction’s commands are named with construction terminology, while
most of XYZ Interiors’ commands are named with interior decorating ter-
minology, but both applications define commands named INVENTORY
and ORDERS. When working on the construction aspects of a drawing, the
user chooses a menu item defined by ABC Construction, and the follow-
ing script runs:
ARX
Group
ABC
The script pops the ABC Construction command set to give it top priority
and to resolve INVENTORY to the ABC Construction version of the com-
mand. Later, when an interior designer is working on the drawing with
the same set of applications loaded, selecting a key icon ensures that the
XYZ Interiors commands have precedence.
■ Classes
Displays a class hierarchy of C++ classes derived from objects registered in
the system, whether registered by AutoCAD or by an ARX program.
■ Services
Lists the names of all services registered by AutoCAD and by loaded ARX
programs.
Error Handling
The examples in this guide have omitted necessary error checking to simplify
the code. However, you’ll always want to check return status and take appro-
priate action. The following example shows appropriate use of error checking
for several examples shown first in chapter 2, “Database Primer.”
Acad::ErrorStatus
createCircle(AcDbObjectId& circleId)
{
circleId = AcDbObjectId::kNull;
AcDbBlockTable *pBlockTable;
Acad::ErrorStatus es =
acdbHostApplicationServices()->workingDatabase()->
getSymbolTable(pBlockTable, AcDb::kForRead);
if (es != Acad::eOk) {
delete pCirc;
return es;
}
AcDbBlockTableRecord *pBlockTableRecord;
es = pBlockTable->getAt(ACDB_MODEL_SPACE,
pBlockTableRecord, AcDb::kForWrite);
if (es != Acad::eOk) {
Acad::ErrorStatus es2 = pBlockTable->close();
if (es2 != Acad::eOk) {
acrx_abort("\nApp X failed to close Block"
" Table. Error: %d",
acadErrorStatusText(es2));
}
delete pCirc;
return es;
}
es = pBlockTable->close();
if (es != Acad::eOk) {
acrx_abort("\nApp X failed to close Block Table."
" Error: %d", acadErrorStatusText(es));
}
es = pBlockTableRecord->appendAcDbEntity(circleId,
pCirc);
if (es != Acad::eOk) {
Acad::ErrorStatus es2 = pBlockTableRecord->close();
if (es2 != Acad::eOk) {
acrx_abort("\nApp X failed to close"
" Model Space Block Record. Error: %s",
acadErrorStatusText(es2));
}
delete pCirc;
return es;
}
es = pBlockTableRecord->close();
if (es != Acad::eOk) {
acrx_abort("\nApp X failed to close"
" Model Space Block Record. Error: %d",
acadErrorStatusText(es));
}
Acad::ErrorStatus
createNewLayer()
{
AcDbLayerTableRecord *pLayerTableRecord
= new AcDbLayerTableRecord;
if (pLayerTableRecord == NULL)
return Acad::eOutOfMemory;
Acad::ErrorStatus es
= pLayerTableRecord->setName("ASDK_MYLAYER");
if (es != Acad::eOk) {
delete pLayerTableRecord;
return es;
}
AcDbLayerTable *pLayerTable;
es = acdbHostApplicationServices()->workingDatabase()->
getSymbolTable(pLayerTable, AcDb::kForWrite);
if (es != Acad::eOk) {
delete pLayerTableRecord;
return es;
}
Error Handling | 57
AcDbObjectId ltypeObjId;
es = pLinetypeTbl->getAt("CONTINUOUS", ltypeObjId);
if (es != Acad::eOk) {
delete pLayerTableRecord;
es = pLayerTable->close();
if (es != Acad::eOk) {
acrx_abort("\nApp X failed to close Layer"
" Table. Error: %d",
acadErrorStatusText(es));
}
return es;
}
pLayerTableRecord->setLinetypeObjectId(ltypeObjId);
es = pLayerTable->add(pLayerTableRecord);
if (es != Acad::eOk) {
Acad::ErrorStatus es2 = pLayerTable->close();
if (es2 != Acad::eOk) {
acrx_abort("\nApp X failed to close Layer"
" Table. Error: %d",
acadErrorStatusText(es2));
}
delete pLayerTableRecord;
return es;
}
es = pLayerTable->close();
if (es != Acad::eOk) {
acrx_abort("\nApp X failed to close Layer"
" Table. Error: %d",
acadErrorStatusText(es));
}
es = pLayerTableRecord->close();
if (es != Acad::eOk) {
acrx_abort("\nApp X failed to close Layer"
" Table Record. Error: %d",
acadErrorStatusText(es));
}
return es;
}
In This Chapter
4
This chapter describes basic database protocol including ■ Initial Database
■ Creating and Populating a
how to create a database, how to read in a drawing file,
Database
and how to save the database. The wblock and insert ■ Saving a Database
■ The wblock Operation
operations are also described here.
■ Inserting a Database
For more detailed information on the deepClone and ■ Setting Current Database Values
■ Example of Database Operations
wblock operations, see chapter 18, “Deep Cloning.”
■ Long Transactions
■ External References
■ Indexes and Filters
■ Drawing Summary Information
■ Last Saved by Autodesk Software
59
Initial Database
When an AutoCAD session begins, the database contains the following
elements:
Saving a Database
To save a database, use the AcDbDatabase::saveAs() function:
Acad::ErrorStatus
AcDbDatabase::saveAs(char* fileName);
The file name can be a path to a local file, or an Internet address.
Save format
Saving a Database | 61
Save format (continued)
■ Either you or your user may set a persistent session-wide default format for
save that will be honored by all save commands except AUTOSAVE.
■ Only the user can temporarily (not persistently between sessions) override
this setting for a particular document.
If conflicts arise when the source and target databases are being merged (for
example, if both databases have the same linetype name), AutoCAD uses the
version in the target database.
The following function is equivalent to a standard drawing INSERT
command:
Acad::ErrorStatus
AcDbDatabase::insert(AcDbObjectId& blockId,
const char* pBlockName,
AcDbDatabase* pDb);
This function copies the entities from the model space of the input database
(pDb) into the specified block table record (pBlockName) and returns the block
ID of the new block table record (blockId). The application must then create
the reference to the block table record and add it to the database.
The following function is equivalent to an AutoCAD INSERT* command:
Acad::ErrorStatus
AcDbDatabase::insert(const AcGeMatrix3d& xform,
AcDbDatabase* pDb);
This function copies the entities from the model space of the input database
(pDb) and puts them into the current space of the new database (paper space
or model space), applying the specified transformation (xform) to the
entities.
Inserting a Database | 65
Setting Current Database Values
If a data property such as color or linetype is not specified for an entity, the
database’s current value for that data is used. The following sections outline
the functions used to specify the current data values associated with the
database.
■ A linetype scale setting for the current entity, stored in the CELTSCALE
system variable.
■ A linetype scale setting for the current drawing, stored in the LTSCALE
system variable.
■ A flag that indicates whether to apply linetype scaling to the space the
entity resides in or to the entity’s appearance in paper space. This setting
is stored in the PSLTSCALE system variable.
Acad::ErrorStatus
AcDbDatabase::setCeltscale(double);
Acad::ErrorStatus
AcDbDatabase::setPsltscale(Adesk::Boolean)
AcDbBlockTable *pBtbl;
pDb->getSymbolTable(pBtbl, AcDb::kForRead);
pBtblRcd->appendAcDbEntity(pCir2);
pCir2->close();
pBtblRcd->close();
void
readDwg()
{
// Set constructor parameter to kFalse so that the
// database will be constructed empty. This way only
// what is read in will be in the database.
//
AcDbDatabase *pDb = new AcDbDatabase(Adesk::kFalse);
AcDbBlockTableRecord *pBlkTblRcd;
pBlkTbl->getAt(ACDB_MODEL_SPACE, pBlkTblRcd,
AcDb::kForRead);
pBlkTbl->close();
AcDbBlockTableRecordIterator *pBlkTblRcdItr;
pBlkTblRcd->newIterator(pBlkTblRcdItr);
delete pBlkTblRcdItr;
delete pDb;
}
Long Transactions
Long Transactions are used to support the AutoCAD Reference Editing fea-
ture and are very useful for ObjectARX applications. These classes and
functions provide a scheme for applications to check out entities for editing
and check them back in to their original location. This operation replaces the
original objects with the edited ones. There are three types of long transac-
tion check out:
■ AcDbLongTransaction class
■ AcDbLongTransWorkSetIterator class
■ AcApLongTransactionReactor class
■ AcApLongTransactionManager class
■ AcDbDatabase::wblockCloneObjects() function
AcDbLongTransaction Class
AcDbLongTransaction is the class that contains the information needed to
track a long transaction. The AcDbLongTransactionManager class takes the
responsibility for creating and appending AcDbLongTransaction objects to
the database. It then returns the AcDbObjectId of the AcDbLongTransaction
Long Transactions | 69
object. Like all other database-resident objects, its destruction is handled by
the database.
AcDbLongTransWorkSetIterator Class
AcDbLongTransWorkSetIterator provides read-only access to the objects in
the work set. During construction in
AcDbLongTransaction::newWorkSetIterator(), it can be set up to include
only the active work set, or include objects added to the work set because
they are referenced by objects in the work set (secondary objects). It can also
handle objects removed from the work set, either by
AcDbLongTransaction::removeFromWorkSet(), or by being erased.
AcApLongTransactionReactor Class
AcApLongTransactionReactor provides notification specific to long transac-
tion operations. It is designed to be used in conjunction with the deep clone
notifications that will also be sent, but will vary depending upon which type
of check out/in is being executed. To connect these notifications with the
deep clone notifications, the AcDbIdMapping object used for cloning can be
retrieved by calling the AcDbLongTransaction::activeIdMap() function.
AcApLongTransactionManager Class
AcApLongTransactionManager is the manager for starting and controlling
long transactions. There is only one for each AutoCAD session, and it is
accessed via a pointer returned by the acapLongTransactionManager object.
AcDbDatabase::wblockCloneObjects() Function
The wblockCloneObjects() function is a member of AcDbDatase. It will deep
clone objects from one database to another and follow hard references so
that all dependent objects are also cloned. The behavior of symbol table
records, when duplicates are found, is determined by the type parameter. The
Long Transactions | 71
// Get a dwg file from the user.
//
rb = acutNewRb(RTSTR);
acedGetFileD("Pick a drawing", NULL, "dwg", 0, rb);
fname = (char*)acad_malloc(strlen(rb->resval.rstring) + 1);
strcpy(fname, rb->resval.rstring);
acutRelRb(rb);
// Get the block table and then the model space record.
//
AcDbBlockTable *pBlockTable;
pDb->getSymbolTable(pBlockTable, AcDb::kForRead);
AcDbBlockTableRecord *pOtherMsBtr;
pBlockTable->getAt(ACDB_MODEL_SPACE, pOtherMsBtr,
AcDb::kForRead);
pBlockTable->close();
// Create an iterator
//
AcDbBlockTableRecordIterator *pIter;
pOtherMsBtr->newIterator(pIter);
AcDbObjectId id = pThisMsBtr->objectId();
pThisMsBtr->close();
// Create the long transaction. This will check all the entities
// out of the external database.
acapLongTransactionManagerPtr()->checkOut(transId,
objIdArray, id);
// Now modify the color of these entities.
//
int colorIndex;
acedGetInt("\nEnter color number to change entities to: ",
&colorIndex);
AcDbObject* pObj;
if (acdbOpenObject(pObj, transId, AcDb::kForRead) == Acad::eOk)
{
// Get a pointer to the transaction
//
AcDbLongTransaction* pLongTrans =
AcDbLongTransaction::cast(pObj);
if (pLongTrans != NULL) {
// Get a work set iterator
//
AcDbLongTransWorkSetIterator* pWorkSetIter =
pLongTrans->newWorkSetIterator();
// Iterate over the entities in the work set
// and change the color.
for (pWorkSetIter->start(); !pWorkSetIter->done();
pWorkSetIter->step()) {
AcDbEntity *pEntity;
acdbOpenAcDbEntity(pEntity, pWorkSetIter->objectId(),
AcDb::kForWrite);
pEntity->setColorIndex(colorIndex);
pEntity->close();
}
delete pWorkSetIter;
}
pObj->close();
}
Long Transactions | 73
// Pause just to see the change.
//
char str[132];
acedGetString(0, "\nNote the new colors. Press return to \
check the objects back in to the original database", str);
// Check the entities back in to the orginal database.
//
acapLongTransactionManagerPtr()->checkIn(transId);
// Save the original database, since we made changes
//
pDb->saveAs(fname);
// Close and Delete the database.
//
delete pDb;
pDb = NULL;
acad_free(fname);
}
External References
External references (xrefs) can be created and manipulated through several
global functions. These global functions mimic the AutoCAD XREF command
capabilities. The functions provided are
■ acedXrefAttach()
■ acedXrefOverlay()
■ acedXrefUnload()
■ acedXrefDetach()
■ acedXrefReload()
■ acedXrefBind()
■ acedXrefXBind()
■ acedXrefCreateBlockname()
■ acedXrefReload()
■ beginAttach()
■ otherAttach()
■ abortAttach()
■ endAttach()
■ redirected()
■ comandeered()
■ AcDbDatabase::xrefBlockId()
■ AcDbDatabase::restoreOriginalXrefSymbols()
■ AcDbDatabase::restoreForwardingXrefSymbols()
External References | 75
AcDbDatabase::xrefBlockId() Function
The xrefBlockId() function will get the AcDbObjectId of the block table
record, which refers to this database as an xref. When an xref is reloaded for
any reason (for example, XREF Reload or XREF Path commands), the former
database is kept in memory for Undo. This means that more than one data-
base may point to the same xref block table record. However only one will be
the currently active xref database for that record. The database pointer
returned by the AcDbBlockTableRecord::xrefDatabase() function on the
found record will be the active database for that xref.
AcDbDatabase::restoreOriginalXrefSymbols() Function
The restoreOriginalXrefSymbols() function restores a resolved xref data-
base to its original form, as it would be if just loaded from its file. The xref is
then in a condition where it can be modified and saved back to a file. After
calling this function, the host drawing is no longer in a valid state for regen
or for any xref command modifications or reloads. The database modifica-
tions, save back, and the restoreForwardingXrefSymbols() function must
be called before anything that might allow a regen.
AcDbDatabase::restoreForwardingXrefSymbols() Function
The restoreForwardingXrefSymbols() function restores the xref back to a
valid, attached state. Not only does it restore the original resolved symbols,
but it also seeks out newly added symbols and resolves them as well. The
restoreForwardingXrefSymbols() function cannot handle newly added,
nested xref block table records unless they already exist and are resolved in
the host drawing.
■ Updating indexes
■ Adding and removing indexes to block table records
■ Adding and removing filters to block references
■ Querying for indexes from block table records
■ Querying for filters from block references
■ Iterating through block table records and visiting only a subset of entities
■ AcDbIndexFilterManager namespace
■ AcDbIndex class
■ AcDbFilter class
■ AcDbFilteredBlockIterator class
■ AcDbCompositeFilteredBlockIterator class
AcDbIndex Class
The AcDbIndex class is the base class for all index objects. AcDbSpatialIndex
and AcDbLayerIndex derive from this class.
Keeping the index up to date is achieved through the
AcDbIndexFilterManager::updateIndexes() function calls being explicitly
invoked (either by an application or AutoCAD).
The AcDbFilteredBlockIterator will serve as the means to visit all the
AcDbObjectIds that are “hits” from the query defined by the AcDbFilter
passed to its constructor. For example, in the spatial index case, the
AcDbSpatialFilter object instance passed to the newIterator() method
will define a query region. The AcDbSpatialIndex object, through its
newIterator() method, will provide an AcDbSpatialIndexIterator that will
return object IDs that correspond to entities that fit within the query
volume.
AcDbFilter class
The AcDbFilter class is meant to define a “query.” It provides the “key” to
the AcDbCompositeFilteredBlockIterator, for which the corresponding
index is obtained through the indexClass() method.
AcDbFilteredBlockIterator Class
The AcDbFilteredBlockIterator class provides a method to process a
“query” on an index. It is used by the
AcDbCompositeFilteredBlockIterator.
AcDbCompositeFilteredBlockIterator Class
The AcDbCompositeFilteredBlockIterator class provides the alternate to
normal block iteration. By providing the filter list in the init() method, the
AcDbCompositeFilteredBlockIterator object looks for corresponding
AcDbIndex derived objects through the AcDbFilter::indexClass() method,
and creates AcDbFilteredBlockIterator objects. If the matching up-to-date
indexClass() objects are not available, it creates an
AcDbFilteredBlockIterator through the AcDbFilter::newIterator()
method. It then orders the composition of the AcDbFilteredBlockIterator
objects based on the AcDbFilteredBlockIterator::estimatedHits() and
AcDbFilteredBlockIterator::buffersForComposition() methods. The col-
AcDbDatabaseSummaryInfo Class
The AcDbDatabaseSummaryInfo class encapsulates a set of character strings
that can be used to add additional information to a DWG file. The maximum
length of these strings is 511 characters. This information is stored and
retrieved in the Summary Information object with specific methods for each
information field. The predefined fields are
■ Title
■ Subject
■ Author
■ Keywords
■ Comments
■ Last saved by
■ Revision number
■ Hyperlink base
You can create your own custom fields in addition to the predefined fields.
These custom fields are stored in a list, and you can manipulate custom fields
either by their name (or key) or position (index) in the list. Custom fields are
indexed starting at 1, and there is no limit to the number of fields you can
create.
AcDbSummaryInfoManager Class
The AcDbSummaryInfoManager class organizes the summary information reac-
tors, with methods to add and remove reactors, and to send notification that
the summary information has changed.
In This Chapter
5
This chapter describes topics that relate to all AutoCAD ■ Opening and Closing Database
Objects
database objects, including entities, symbol table
■ Deleting Objects
81
Opening and Closing Database Objects
Each AcDbObject object can be referred to in three different ways:
■ By its handle
■ By its object ID
■ By a C++ instance pointer
When AutoCAD is not running, the drawing is stored in the file system.
Objects contained in a DWG file are identified by their handles.
After the drawing is opened, the drawing information is accessible through
the AcDbDatabase object. Each object in the database has an object ID, which
persists throughout the current edit session, from creation until deletion of
the AcDbDatabase in which the object resides. The open functions take an
object ID as an argument and return a pointer to an AcDbObject object. This
pointer is valid until the object is closed, as shown in the following figure.
DWG
Handle open drawing
SAVE or
WBLOCK AcDbDatabase
command ObjectID open object
close object
C++
Pointer
pObject->getAcDbHandle(handle);
AcDbEntity*
selectEntity(AcDbObjectId& eId, AcDb::OpenMode openMode)
{
ads_name en;
ads_point pt;
acedEntSel("\nSelect an entity: ", en, pt);
AcDbEntity * pEnt;
acdbOpenObject(pEnt, eId, openMode);
return pEnt;
}
The following table shows the error codes returned when you attempt to
open an object in different modes and the object is already open.
If you are trying to open an object for write and you receive an error
eWasOpenForRead, you can use upgradeOpen() to upgrade the open status to
write if there is only one reader of the object. Then you would use
downgradeOpen() to downgrade its status to read. Similarly, if your object is
open for notify—for example, when you are receiving notification—and you
want to open it for write, you can use upgradeFromNotify() to upgrade its
open status to write. Then you would use downgradeToNotify() to down-
grade its status to notify.
For more information about how to manage complex sequences of opening
and closing objects, see “Transaction Manager” on page 451.
Deleting Objects | 85
Adding Object-Specific Data
You can use any of four mechanisms for adding instance-specific data in your
application:
Extended Data
Extended data (xdata) is created by applications written with ObjectARX or
AutoLISP and can be added to any object. Xdata consists of a linked list of
resbufs used by the application. (AutoCAD maintains the information but
doesn’t use it.) The data is associated with a DXF group code in the range of
1000 to 1071.
This mechanism is space-efficient and can be useful for adding lightweight
data to an object. However, xdata is limited to 16K and to the existing set of
DXF group codes and types.
For a more detailed description of xdata, see the
AutoCAD Customization Guide.
Use the AcDbObject::xData() function to obtain the resbuf chain contain-
ing a copy of the xdata for an object:
virtual resbuf*
AcDbObject::xData(const char* regappName = NULL) const;
Use the AcDbObject::setXData() function to specify the xdata for an object:
virtual Acad::ErrorStatus
AcDbObject::setXData(const resbuf* xdata);
The following example uses the xData() function to obtain the xdata for a
selected object and then prints the xdata to the screen. It then adds a string
(testrun) to the xdata and calls the setXdata() function to modify the
object’s xdata. This example also illustrates the use of the upgradeOpen() and
downgradeOpen() functions.
void
addXdata()
{
AcDbObject* pObj = selectObject(AcDb::kForRead);
if (!pObj) {
acutPrintf("Error selecting object\n");
return;
}
pRb = pObj->xData(appName);
if (pRb != NULL) {
// If xdata is present, then walk to the
// end of the list.
//
for (pTemp = pRb; pTemp->rbnext != NULL;
pTemp = pTemp->rbnext)
{ ; }
} else {
// If xdata is not present, register the application
// and add appName to the first resbuf in the list.
// Notice that there is no -3 group as there is in
// AutoLISP. This is ONLY the xdata so
// the -3 xdata-start marker isn’t needed.
//
acdbRegApp(appName);
pRb = acutNewRb(AcDb::kDxfRegAppName);
pTemp = pRb;
pTemp->resval.rstring
= (char*) malloc(strlen(appName) + 1);
strcpy(pTemp->resval.rstring, appName);
}
pObj->close();
acutRelRb(pRb);
}
pObj = selectObject(AcDb::kForWrite);
if (pObj == NULL) {
return;
}
printList(pRbList);
acutRelRb(pRbList);
}
Global Function Example
The following example uses global ObjectARX functions to create an xrecord
and add it to the dictionary associated with the key ASDK_REC.
int
createXrecord()
{
struct resbuf *pXrec, *pEnt, *pDict, *pTemp, *pTemp2;
ads_point dummy, testpt = {1.0, 2.0, 0.0};
ads_name xrecname, ename, extDict = {0L, 0L};
pEnt = acdbEntGet(ename);
pTemp2->rbnext = pTemp->rbnext;
pTemp->rbnext = pDict;
acdbEntMod(pEnt);
acutRelRb(pEnt);
}
pEnt = acdbEntGet(ename);
if (extDict[0] == 0L) {
acutPrintf("\nNo extension dictionary present.");
return RTNORM;
}
acedRetVoid();
return RTNORM;
}
NOTE The erase() function has different results for database objects and
entities, with consequences for unerasing them:
Container objects such as polylines and block table records usually provide
the option of skipping erased elements when iterating over their contents.
The default behavior is to skip erased elements.
Erased objects are not filed out to DWG or DXF files.
AcDbObject has two member functions for filing out: dwgOut() and
dxfOut(), and two member functions for filing in: dwgIn() and dxfIn().
These member functions are primarily called by AutoCAD; object filing is
almost never explicitly controlled by applications that use the database.
However, if your application implements new database object classes, you’ll
need a more in-depth understanding of object filing. See chapter 12,
“Deriving from AcDbObject.”
The dwg- and dxf- prefixes indicate two fundamentally different data
formats, the first typically used in writing to and from DWG files, and the
second primarily for DXF files and AutoLISP entget, entmake, and entmod
functions. The primary difference between the two formats is that for DWG
filers (an object that writes data to a file), the data is not explicitly tagged.
The DXF filers, in contrast, associate a data group code with every element of
data in a published data format (see chapter 12, “Deriving from
AcDbObject”).
Object Filing | 95
96
Entities
In This Chapter
6
This chapter describes entities—database objects with a ■ Entities Defined
■ Entity Ownership
graphical representation. It lists the properties and
■ AutoCAD Release 12 Entities
operations all entities have in common. Examples show ■ Common Entity Properties
how to create blocks, inserts, and complex entities, and ■ Common Entity Functions
■ Creating Instances of AutoCAD
how to select and highlight subentities. Entities
■ Complex Entities
■ Coordinate System Access
■ Curve Functions
■ Associating Hyperlinks with
Entities
97
Entities Defined
An entity is a database object that has a graphical representation. Examples
of entities include lines, circles, arcs, text, solids, regions, splines, and
ellipses. The AcDbEntity class is derived from AcDbObject.
With a few exceptions, entities contain all necessary information about their
geometry. A few entities contain other objects that hold their geometric
information or attributes. Complex entities include the following:
98 | Chapter 6 Entities
Entity Ownership
Entities in the database normally belong to an AcDbBlockTableRecord. The
block table in a newly created database has three predefined records,
*MODEL_SPACE, *PAPER_SPACE, and *PAPER_SPACE0, which represent
model space and the two pre-defined paper space layouts. Additional records
are added whenever the user creates new blocks (block records), typically by
issuing a BLOCK, HATCH, or DIMENSION command.
The ownership structure for database entities is as follows:
Entity Ownership | 99
AcDbDatabase
AcDbBlockTable
AcDbBlockTableRecord
AcDbxxxVertex or AcDbSequenceEnd
AcDbFaceRecord or
AcDbAttribute
■ AcDb2dPolyline
■ AcDb3dPolyline
■ AcDbPolygonMesh
■ AcDbPolyFaceMesh
■ AcDbSequenceEnd
■ AcDbBlockBegin
■ AcDbBlockEnd
■ AcDbVertex
■ AcDbFaceRecord
■ AcDb2dVertex
■ AcDb3dPolylineVertex
■ AcDbPolygonMeshVertex
■ AcDbPolyFaceMeshVertex
■ AcDbMInsertBlock
■ Color
■ Linetype
■ Linetype scale
■ Visibility
■ Layer
■ Line weight
■ Plot style name
If a property has not been explicitly specified for an entity, the database’s cur-
rent value for that property is used. See chapter 4, “Database Operations,” for
a description of the member functions used for setting and getting the cur-
rent property values associated with the database.
Entity Color
Entity color can be set and read as numeric index values ranging from 0 to
256, or by instances of AcCmColor, which is provided for future use by an
expanded color model. Currently, AutoCAD uses color indexes only. The cor-
rect color index can be obtained from an instance of AcCmColor using the
AcCmColor::getColorIndex() member function.
Color indexes 1 through 7 are used for standard colors, as shown in the fol-
lowing table:
Colors 1 to 7
1 Red
2 Yellow
3 Green
4 Cyan
5 Blue
6 Magenta
7 White or Black
Adesk::UInt16
AcDbEntity::colorIndex() const;
Entity Linetype
The linetype value points to a symbol table entry that specifies a series of dots
and dashes used for drawing lines. When an entity is instantiated, its line-
type is set to NULL. When the entity is added to the database, if a linetype has
not been specified for the entity, the linetype is set to the database’s current
linetype value. This default value is stored in the CELTYPE system variable.
Linetype can be specified by name, by a string, or by the object ID of an
AcDbLineTypeTableRecord in the entity’s target database.
virtual Acad::ErrorStatus
AcDbEntity::setLinetype(AcDbObjectId newVal);
This function returns the name of the current entity linetype:
char* AcDbEntity::linetype() const;
This function returns the object ID for the symbol table record specifying the
linetype:
AcDbObjectId AcDbEntity::linetypeId() const;
double
AcDbEntity::linetypeScale() const;
Entity Visibility
If you specify that an entity is invisible, it will be invisible regardless of other
settings in the database. Other factors can also cause an entity to be invisible.
For example, an entity will not be displayed if its layer is turned off or frozen.
The value of AcDb::Visibility can be either kInvisible or kVisible.
Acad::ErrorStatus
AcDbEntity::setVisibility(AcDb::Visibility newVal);
AcDb::Visibility
AcDbEntity::visibility() const;
Entity Layer
All entities have an associated layer. The database always contains at least
one layer (layer 0). As with linetypes, you can specify a layer for an entity. If
you don’t specify a layer, the default database layer value is used for a new
entity.
Each layer also has associated properties, which include frozen/thawed,
on/off, locked/unlocked, color, linetype, and viewport (see chapter 7,
“Container Objects”). When an entity’s color or linetype is BYLAYER, the
value of the layer property is used for the entity.
If a layer value is specified for an entity, the current database layer value is
ignored.
The following functions enable you to set the layer for an entity, either by
name or by object ID:
Acad::ErrorStatus
AcDbEntity::setLayer(const char* newVal);
Acad::ErrorStatus
AcDbEntity::setLayer(AcDbObjectId newVal);
Mode Description
kOsModeEnd Endpoint
kOsModeMid Midpoint
kOsModeCen Center
kOsModeNode Node
kOsModeQuad Quadrant
kOsModeIns Insertion
kOsModePerp Perpendicular
kOsModeTan Tangent
kOsModeNear Nearest
Transform Functions
The AcDbEntity class provides two transformation functions:
virtual Acad::ErrorStatus
AcDbEntity::transformBy(const AcGeMatrix3d& xform);
virtual Acad::ErrorStatus
AcDbEntity::getTransformedCopy(const AcGeMatrix3d& xform,
AcDbEntity*& ent) const;
The transformBy() function modifies the entity using the specified matrix.
In AutoCAD, it is called by the grip move, rotate, scale, and mirror modes. In
some cases, however, applying the transformation requires that a new entity
be created. In such cases, the getTransformedCopy() function is used so that
the resulting entity can be an instance of a different class than the original
entity.
When you explode a block reference that has been nonuniformly scaled, the
getTransformedCopy() function is called on the entities in the block refer-
ence to create the new entities (see “Exploding Entities” on page 123).
For example, suppose a drawing contains the three lines shown in the
following illustration. Line1 is “this” and line3 is the argument entity. If the
line2
virtual Acad::ErrorStatus
AcDbEntity::intersectWith(
const AcDbEntity* ent,
AcDb::Intersect intType,
const AcGePlane& projPlane,
AcGePoint3dArray& points,
int thisGsMarker = 0,
int otherGsMarker = 0) const;
The returned points are always on the entity (“this”). Therefore, in cases of
apparent intersection, the intersected points are projected back to the entity
before they are returned.
Both versions of the intersectWith() function allow you to supply optional
GS markers to optimize performance for this function. If the entity’s
intersectWith() function has implemented the use of GS markers, then
3
2 12
1
9 10 8 11
6
5 7
4
Subentity Path
A subentity path uniquely identifies a subentity within a particular entity in
a drawing. This path, of class AcDbFullSubentPath, consists of an array of
object IDs and a subentity ID object:
{AcDbObjectIdArray mObjectIds;
AcDbSubentId mSubentId;
}
The array contains the object IDs that specify the path to the “main” entity.
For example, a block reference (an entity that references a block table record)
might contain two boxes, each of type AcDb3dSolid. The object ID array con-
tains two entries: the ID of the block reference, followed by the ID of the
main entity [InsertID, SolidID].
The second element of an AcDbFullSubentPath is an AcDbSubentId object,
which has a subentity type (vertex, edge, or face) and the index of the sub-
entity in the list. Use the AcDbSubentId functions type() and index() to
access the member data.
Using the previous example, the second edge of the solid will have its
AcDbFullSubentPath as
{(InsertID, solid1ID)
(kEdgeSubentType, 2)};
If you have a solid only, AcDbFullSubentPath would be as follows for the first
face of the solid.
{(solidID)
(kFaceSubentType, 1)};
To highlight a subentity
1 Obtain the GS marker for the selected entity from the selection set.
2 Pass the GS marker to the entity class to be converted to a subentity path
using the getSubentPathsAtGsMarker() function. Specify the type of suben-
tity you’re interested in (vertex, edge, face).
3 Once you have the path to the selected subentity, you’re ready to call the
highlight() function, passing in the correct subentity path.
Selecting an Entity
For selection, you’ll use a combination of global functions. First, use the
acedSSGet() function to obtain the selection set. Then, use the
acedSSNameX() function to obtain a subentity GS marker for the selected
entity.
int acedSSGet(
const char *str,
const void *pt1,
const ads_point pt2,
const struct resbuf *entmask,
ads_name ss);
int acedSSNameX(
struct resbuf** rbpp,
const ads_name ss,
const longvi);
Converting GS Markers to Subentity Paths
Use the getSubentPathsAtGsMarker() function to obtain the subentity for
the GS marker returned by the acedSSNameX() function. The complete syntax
for this function is
virtual Acad::ErrorStatus
AcDbEntity::getSubentPathsAtGsMarker(
AcDb::SubentType type,
int gsMark,
const AcGePoint3d& pickPoint,
const AcGeMatrix3d& viewXform,
int& numPaths,
AcDbFullSubentPath*& subentPaths
int numInserts = 0,
AcDbObjectId* entAndInsertStack = NULL) const;
The first argument to this function is the type of subentity you’re interested
in (vertex, edge, or face). In the example code in “Highlighting the Suben-
tity,” the first call to this function specifies kEdgeSubentType because you’re
return Acad::eOk;
}
pEnt->getSubentPathsAtGsMarker(AcDb::kEdgeSubentType,
marker, pickpnt, xform, numIds, subentIds);
// At this point the subentId’s variable contains the
// address of an array of AcDbFullSubentPath objects.
// The array should be one element long, so the picked
// edge’s AcDbFullSubentPath is in subentIds[0].
//
// For objects with no edges (such as a sphere), the
// code to highlight an edge is meaningless and must
// be skipped.
//
if (numIds > 0) {
// Highlight the edge.
//
pEnt->highlight(subentIds[0]);
pEnt->getSubentPathsAtGsMarker(AcDb::kFaceSubentType,
marker, pickpnt, xform, numIds, subentIds);
Acad::ErrorStatus
addToModelSpace(AcDbObjectId &objId, AcDbEntity* pEntity)
{
AcDbBlockTable *pBlockTable;
AcDbBlockTableRecord *pSpaceRecord;
acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pBlockTable, AcDb::kForRead);
pBlockTable->getAt(ACDB_MODEL_SPACE, pSpaceRecord,
AcDb::kForWrite);
pSpaceRecord->appendAcDbEntity(objId, pEntity);
pBlockTable->close();
pEntity->close();
pSpaceRecord->close();
return Acad::eOk;
ins1
ins2
ins3
void
createInsert()
{
// Create a nested insert and try highlighting its
// various subcomponents.
//
// There are six entities in total -- three polys and
// three boxes (solids). We’ve named them: poly1, poly2,
// poly3, and box1, box2, box3. We also have three
// inserts: ins1, ins2, ins3.
//
// ins3 is an insert of a block that contains (poly3, box3)
// ins2 is an insert of a block that contains (poly2, box2,
// ins3).
// ins1 is an insert of a block that contains (poly1, box1,
// ins2).
//
// Let's create these entities first.
//
// Polys
//
AsdkPoly *poly1, *poly2, *poly3;
AcGeVector3d norm(0, 0, 1);
if ((poly1=new AsdkPoly)==NULL){
acutPrintf("\nOut of Memory.");
return;
}
if (poly1->set(AcGePoint2d(2, 8),AcGePoint2d(4, 8), 6, norm,
"POLY1",0)!=Acad::eOk){
acutPrintf("\nCannot create object with given parameters.");
delete poly1;
return;
}
// Boxes
//
AcDb3dSolid *box1, *box2, *box3;
box1 = new AcDb3dSolid();
box2 = new AcDb3dSolid();
box3 = new AcDb3dSolid();
box1->createBox(2, 2, 2);
box2->createBox(2, 2, 2);
box3->createBox(2, 2, 2);
AcGeMatrix3d mat;
mat(0, 3) = 2; mat(1, 3) = 2;
box1->transformBy(mat);
mat(0, 3) = 7; mat(1, 3) = 2;
box2->transformBy(mat);
mat(0, 3) = 12; mat(1, 3) = 2;
box3->transformBy(mat);
postToDb(box1);
postToDb(box2);
postToDb(box3);
void
hilitInsert()
{
Adesk::Boolean interrupted = Adesk::kFalse;
acutPrintf("\nSelect an insert");
Acad::ErrorStatus es = Acad::eOk;
AcDbEntity *ent = NULL;
AcDbEntity *ent2 = NULL;
AcDbBlockReference *blRef = NULL;
AcDbObjectId objectId, blRefId;
ads_name ename, sset;
int sel_method;
ads_name subname;
short marker;
AcGePoint3d pickpnt;
AcGeVector3d pickvec;
if (!extractEntityInfo(rb,
sel_method,
ename,
subname,
marker,
pickpnt,
pickvec)) {
acutPrintf("\nextractEntityInfo failed");
acedSSFree(sset);
return;
}
acedSSFree(sset);
assert(marker != 0);
if (marker == 0) {
acutPrintf("\nmarker == 0");
return;
}
count--;
acutRelRb(insStack);
AcDbFullSubentPath subPath;
for (int i = count; i >= 0; i--) {
subPath.objectIds().append(idArray[i]);
}
es = blRef->highlight(subPath);
pressEnterToContinue();
es = blRef->unhighlight(subPath);
Exploding Entities
Some entities can be exploded, or decomposed, into a set of simpler ele-
ments. The specific behavior depends on the class. For example, boxes can be
exploded into regions, then lines. Polylines can be exploded into line seg-
ments. An mtext entity can be exploded into a separate text entity for each
line of the original object. An mline entity can be exploded into individual
lines. When you explode a block reference, AutoCAD copies all entities in the
block reference and then splits them into their components.
The explode() function creates an array of objects derived from AcDbEntity.
The following table shows what happens when you explode each entity,
when it is by itself and when it is in a block insert that is nonuniformly
scaled.
Exploding entities
Nonuniform Scaling
Entity By Itself (when in a block)
Nonuniform Scaling
Entity By Itself (when in a block)
AcDbLeader Self NA
The explode() function is a read-only function that does not modify the
original entity. It returns a set of entities for the application to handle as
desired. One potential use of this function is to explode a complex entity to
AcDbBlockTable *pBlockTable;
acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pBlockTable, AcDb::kForRead);
AcDbBlockTableRecord *pBlockTableRecord;
pBlockTable->getAt(ACDB_MODEL_SPACE, pBlockTableRecord,
AcDb::kForWrite);
pBlockTable->close();
AcDbObjectId lineId;
pBlockTableRecord->appendAcDbEntity(lineId, pLine);
pBlockTableRecord->close();
pLine->close();
return lineId;
}
pBlockTableRec->appendAcDbEntity(lineId, pLine);
pLine->close();
pBlockTableRec->close();
}
When you close the block table record, the block begin and block end objects
are added to the block automatically.
The following example creates a new block table record named
ASDK-BLOCK-WITH-ATTR and adds it to the block table. Next it creates a circle
entity and adds it to the new block table record. It creates two attribute defi-
nition entities (the second is a clone of the first) and appends them to the
same block table record.
void
defineBlockWithAttributes(
AcDbObjectId& blockId, // This is a returned value.
const AcGePoint3d& basePoint,
double textHeight,
double textAngle)
{
int retCode = 0;
AcDbBlockTable *pBlockTable = NULL;
AcDbBlockTableRecord* pBlockRecord = new AcDbBlockTableRecord;
AcDbObjectId entityId;
// Step 1: Set the block name and base point of the
// block definition.
//
pBlockRecord->setName("ASDK-BLOCK-WITH-ATTR");
pBlockRecord->setOrigin(basePoint);
// Indicate the LCS 0.0 angle, not necessarily the UCS 0.0 angle.
//
pBlkRef->setRotation(0.0);
pBlkRef->setNormal(normal);
AcDbBlockTable *pBlockTable;
acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pBlockTable, AcDb::kForRead);
AcDbBlockTableRecord *pBlockTableRecord;
pBlockTable->getAt(blkName, pBlockTableRecord,
AcDb::kForRead);
pBlockTable->close();
AcDbBlockTableRecordIterator *pBlockIterator;
pBlockTableRecord->newIterator(pBlockIterator);
AcDbHandle objHandle;
pEntity->getAcDbHandle(objHandle);
char handleStr[20];
objHandle.getIntoAsciiBuffer(handleStr);
const char *pCname = pEntity->isA()->name();
Complex Entities
This section provides examples showing how to create and iterate through
complex entities.
location = pVertex->position();
pVertex->close();
■ Dimensions
■ Text
■ Circles
■ Arcs
■ 2D polylines
■ Block inserts
■ Points
■ Traces
■ Solids
■ Shapes
■ Attribute definitions
■ Attributes
AcDb2dPolylineVertex
An AcDb2dPolyline has as an elevation and a series of X,Y points of class
AcDb2dPolylineVertex. The position() and setPosition() functions of
AcDb2dPolylineVertex specify 3D locations in the ECS. The Z coordinate
passed in to the setPosition() function is stored in the entity and is
returned by the position() function, but is otherwise ignored. It does not
affect the polyline’s elevation.
The AcDb2dPolyline class provides the vertexPosition() function, which
returns a World Coordinate System value for the vertex passed in. The only
way to change the elevation of a polyline is using the
AcDb2dPolyline::setElevation() function.
Curve Functions
The abstract base class AcDbCurve provides a number of functions for operat-
ing on curves, including functions for projecting, extending, and offsetting
curves, as well as a set of functions for querying curve parameters. Curves can
be defined either in parameter space or in Cartesian coordinate space. A 3D
curve is a function of one parameter (f(t)), while a 3D surface is a function of
two parameters (f(u,v)). Conversion functions allow you to convert data from
its parameter representation to points in the Cartesian coordinate system.
Splines, for example, are best represented in parameter space. To split a spline
into three equal parts, you first find the parameters that correspond to the
points of the spline and then operate on the spline in parameter space.
AcDbObjectId newCurveId;
addToModelSpace(newCurveId, pProjectedCurve);
}
AcDbObjectId newCurveId;
addToModelSpace(newCurveId, (AcDbEntity*)curves[0]);
}
AcDbHyperlink Class
An AcDbHyperlink object contains the hyperlink name (for example,
http://www.autodesk.com), a sublocation within that link, the hyperlink
description or friendly name (“Click here for Autodesk”), and a display string
for the hyperlink. For AutoCAD, a sublocation is a named view, while in a
spreadsheet application, for example, a sublocation might be a cell or group
of cells. The display string is usually the same as the hyperlink’s description.
If the description is null, the hyperlink’s name and sublocation are used
instead, in “name – sublocation” format.
Hyperlinks may also have nesting levels. Nesting level is only of interest
when dealing with hyperlink collections associated with an entity within a
block, or with collections associated with an INSERT entity.
AcDbHyperlinkCollection Class
This class is a collection of AcDbHyperlink objects, and has a variety of meth-
ods for adding and removing those objects. The AcDbHyperlinkCollection
deletes its contents when they are removed, and when the collection object
itself is deleted. Hyperlinks in the collection are numbered from zero.
AcDbEntityHyperlinkPE Class
The methods of the AcDbEntityHyperlinkPE class allow you to set, get, and
count the hyperlinks associated with an entity.
In This Chapter
7
This chapter describes the container objects used in ■ Comparison of Symbol Tables and
Dictionaries
AutoCAD database operations: symbol tables,
■ Symbol Tables
“Database Objects.”
143
Comparison of Symbol Tables and
Dictionaries
Symbol tables and dictionaries perform essentially the same function; they
contain entries that are database objects that can be looked up using a text
string key. You can add entries to these container objects, and you can use an
iterator to step through the entries and query their contents.
The AutoCAD database always contains a fixed set of nine symbol tables,
described in the following section. You cannot create or delete a symbol
table, but you can add or change the entries in a symbol table, which are
called records. Each symbol table contains only a particular type of object.
For example, the AcDbLayerTable contains only objects of type
AcDbLayerTableRecord. Symbol tables are defined in this manner mainly for
compatibility with AutoCAD Release 12 and previous releases of AutoCAD.
Dictionaries provide a similar mechanism for storing and retrieving objects
with associated name keys. The AutoCAD database creates the named object
dictionary whenever it creates a new drawing. The named object dictionary
can be viewed as the master “table of contents” for the nonentity object
structures in a drawing. This dictionary, by default, contains four
dictionaries: the GROUP dictionary, the MLINE style dictionary, the layout dic-
tionary, and the plot style name dictionary. You can create any number of
additional objects and add them to the named object dictionary. However,
the best practice is to add one object directly to the named object dictionary
and have that object in turn own the other objects associated with your
application. Typically, the owning object is a container class such as a
dictionary. Use your assigned four-letter Registered Developer Symbol for the
name of this class.
An AcDbDictionary object can contain any type of AcDbObject, including
other dictionaries. A dictionary object does not perform type checking of
entries. However, the MLINE style dictionary should contain only instances
of class AcDbMlineStyle, and the GROUP dictionary should contain only
instances of AcDbGroup. An application may require specific typing for entries
in a dictionary that it creates and maintains.
AcDbDictionary
AcDbDictionarywithDefault
Symbol Table
Dictionary
<name> Object
<class-specific fields>
Each symbol table class provides a getAt() function for looking up the
record specified by name. The signatures for overloaded forms of the getAt()
function are as follows. (##BASE_NAME## stands for any of the nine symbol
table class types.)
Acad::ErrorStatus
AcDb##BASE_NAME##Table::getAt(const char* pEntryName,
AcDb::OpenMode mode,
AcDb##BASE_NAME##TableRecord*&
pRecord,
Adesk::Boolean openErasedRecord =
Adesk::kFalse) const;
or
Acad::ErrorStatus
AcDb##BASE_NAME##Table::add(AcDbObjectId& recordId,
AcDb##BASE_NAME##TableRecord*
pRecord);
This function adds the record pointed to by pRecord to both the database
containing the table and the table itself. If the additions succeed and the
argument pId is non-NULL, it is set to the AcDbObjectId of the record in the
database.
Layer Table
The layer table contains one layer, layer 0, by default. A user adds layers to
this table with the LAYER command.
Layer Properties
The AcDbLayerTableRecord class contains member functions for specifying a
number of layer properties that affect the display of their associated entities.
All entities must refer to a valid layer table record. The AutoCAD User’s Guide
provides a detailed description of layer properties.
The following sections list the member functions for setting and querying
layer properties.
Frozen/Thawed
When a layer is frozen, graphics are not regenerated.
void AcDbLayerTableRecord::setIsFrozen(Adesk::Boolean);
Adesk::Boolean
AcDbLayerTableRecord::isFrozen() const;
Adesk::Boolean
AcDbLayerTableRecord::isOff() const;
Viewport
This setVPDFLT() function specifies whether the layer by default is visible or
invisible in new viewports.
void AcDbLayerTableRecord::setVPDFLT(Adesk::Boolean);
Adesk::Boolean
AcDbLayerTableRecord::VPDFLT() const;
Locked/Unlocked
Entities on a locked layer cannot be modified by an AutoCAD user or opened
for the write() function within a program.
void AcDbLayerTableRecord::setIsLocked(Adesk::Boolean);
Adesk::Boolean
AcDbLayerTableRecord::isLocked() const;
Color
The color set by the setColor() function is used when an entity’s color is
BYLAYER.
void AcDbLayerTableRecord::setColor(const AcCmColor &color);
AcCmColor
AcDbLayerTableRecord::color() const;
Linetype
The linetype set by the setLinetypeObjectId() function is used when an
entity’s linetype is BYLAYER.
void AcDbLayerTableRecord::setLinetypeObjectId(AcDbObjectId);
AcDbObjectId
AcDbLayerTableRecord::linetypeObjectId() const;
AcCmColor color;
color.setColorIndex(1); // set color to red
pLayerTblRcd->setColor(color);
pLayerTblRcd->setLinetypeObjectId(ltId);
pLayerTbl->add(pLayerTblRcd);
pLayerTblRcd->close();
pLayerTbl->close();
} else {
pLayerTbl->close();
acutPrintf("\nlayer already exists");
}
}
Acad::ErrorStatus
AcDb##BASE_NAME##Table::newIterator(
AcDb##BASE_NAME##TableIterator*& pIterator,
Adesk::Boolean atBeginning = Adesk::kTrue,
Adesk::Boolean skipErased = Adesk::kTrue) const;
The newIterator() function creates an object that can be used to step
through the contents of the table and sets pIterator to point to the iterator
object. If atBeginning is kTrue, the iterator starts at the beginning of the
table; if kFalse, it starts at the end of the table. If the skipErased argument
is kTrue, the iterator is positioned initially at the first (or last) unerased
record; if kFalse, it is positioned at the first (or last) record, regardless of
whether it has been erased. For a description of the functions available for
each iterator class, see the ObjectARX Reference.
When you create a new iterator, you are also responsible for deleting it. A
symbol table should not be closed until all of the iterators it has constructed
have been deleted.
In addition to the symbol tables, the block table record has an iterator that
operates on the entities it owns. The AcDbBlockTableRecord class returns an
object of class AcDbBlockTableRecordIterator when you ask it for a new
iterator. This iterator enables you to step through the entities contained in
the block table record and to seek particular entities.
Dictionaries
To create a new dictionary, you need to create an instance of AcDbDictionary,
add it to the database, and register it with its owner object. Use the setAt()
function of AcDbDictionary to add objects to the dictionary and the data-
base. The signature of this function is
Acad::ErrorStatus
AcDbDictionary::setAt(const char* pSrchKey,
AcDbObject* pNewValue,
AcDbObjectId& retObjId);
The setAt() function adds a new entry specified by newValue to the
dictionary. If the entry already exists, it is replaced by the new value. The
name of the object is specified by srchKey. The object ID of the entry is
returned in retObjId.
When you add an entry to a dictionary, the dictionary automatically attaches
a reactor to the entry. If the object is erased, the dictionary is notified and
removes it from the dictionary.
Dictionaries | 153
Use the AcDbGroup::newIterator() function to obtain an iterator and step
through the entities in the group. The AcDbGroup class also provides
functions for appending and prepending entities to the group, inserting enti-
ties at a particular index in the group, removing entities, and transferring
entities from one position in the group to another. See AcDbGroup in the
ObjectARX Reference.
You can also assign properties to all members of a group using the
setColor(), setLayer(), setLinetype(), setVisibility(), and
setHighlight() functions of the AcDbGroup class. These operations have the
same effect as opening each entity in the group and setting its property
directly.
Groups should always be stored in the GROUP dictionary, which can be
obtained as follows:
AcDbDictionary* pGrpDict =
acdbHostApplicationServices()->working Database()->
getGroupDictionary(pGroupDict, AcDb::kForWrite);
An alternative way to obtain the GROUP dictionary is to look up
“ACAD_GROUP” in the named object dictionary.
The following functions are part of an application that first prompts the user
to select some entities that are placed into a group called
“ASDK_GROUPTEST”. Then it calls the function removeAllButLines() to
iterate over the group and remove all the entities that are not lines. Finally,
it changes the remaining entities in the group to red.
void
groups()
{
AcDbGroup *pGroup = new AcDbGroup("grouptest");
AcDbDictionary *pGroupDict;
acdbHostApplicationServices()->workingDatabase()
->getGroupDictionary(pGroupDict, AcDb::kForWrite);
AcDbObjectId groupId;
pGroupDict->setAt("ASDK_GROUPTEST", pGroup, groupId);
pGroupDict->close();
pGroup->close();
makeGroup(groupId);
removeAllButLines(groupId);
}
if (err != RTNORM) {
return;
}
AcDbGroup *pGroup;
acdbOpenObject(pGroup, groupId, AcDb::kForWrite);
Dictionaries | 155
if (!pObj->isKindOf(AcDbLine::desc())) {
// AcDbGroup::remove() requires that the object
// to be removed be closed, so close it now.
//
pObj->close();
pGroup->remove(pIter->objectId());
} else {
pObj->close();
}
}
delete pIter;
<STANDARD>
<MYSTYLE>
AcDbMline::setStyle( )
<name>
Layout Dictionary
The layout dictionary is a default dictionary within the named object dictio-
nary that contains objects of class AcDbLayout. The AcDbLayout object stores
the characteristics of a paper space layout, including the plot settings. Each
AcDbLayout object also contains an object ID of an associated block table
record, which stores the entities associated with the layout.
Entity Layout
Creating a Dictionary
The following example creates a new dictionary (ASDK_DICT) and adds it to
the named object dictionary. Then it creates two new objects of the custom
class AsdkMyClass (derived from AcDbObject) and adds them to the dictio-
nary using the setAt() function.
NOTE You need to close the objects after adding them with the setAt()
function.
Dictionaries | 157
AcDbDictionary *pDict;
if (pNamedobj->getAt("ASDK_DICT", (AcDbObject*&) pDict,
AcDb::kForWrite) == Acad::eKeyNotFound)
{
pDict = new AcDbDictionary;
AcDbObjectId DictId;
pNamedobj->setAt("ASDK_DICT", pDict, DictId);
}
pNamedobj->close();
if (pDict) {
// Create new objects to add to the new dictionary,
// add them, then close them.
//
AsdkMyClass *pObj1 = new AsdkMyClass(1);
AsdkMyClass *pObj2 = new AsdkMyClass(2);
pObj1->close();
pObj2->close();
pDict->close();
}
}
Layouts
AutoCAD initially contains three layouts: a model space layout and two
paper space layouts. These layouts can be accessed by tabs at the bottom of
the drawing window in AutoCAD. The tabs are initially named Model,
Layout1, and Layout2.
The Model tab is the default tab and represents model space, in which you
generally create your drawing. The Layout1 and Layout2 tabs represent paper
space and are generally used for laying out your drawing for printing. The
paper space layouts display a paper image that shows the printable boundary
for the configured print device.
It is recommended that you use paper space layouts for preparing final draw-
ings for output, but printing can be performed from any layout, including
the model space layout. For more information on using layouts in AutoCAD,
see the AutoCAD User’s Guide.
■ AcDbLayout
■ AcDbPlotSettings
■ AcDbPlotSettingsValidator
■ AcDbLayoutManager
■ AcApLayoutManager
■ AcDbLayoutManagerReactor
Layouts | 159
layout objects and to perform other layout-related tasks. The following sec-
tions provide an overview of some of these classes. For more information, see
the ObjectARX Reference. For an example of using the ObjectARX layout
classes, see the lmgrtest.arx sample application in the ObjectARX samples
directory.
Layout Objects
Information about layouts is stored in instances of the AcDbLayout class. A
layout object contains the printing and plotting settings information needed
to print the desired portion of the drawing. For example, a layout object con-
tains the plot device, media size, plot area, and plot rotation, as well as sev-
eral other attributes that help define the area to be printed.
AcDbLayout objects are stored in the ACAD_LAYOUT dictionary within the
named object dictionary of the database. There is one AcDbLayout object per
paper space layout, as well as a single AcDbLayout for model space. Each
AcDbLayout object contains the object ID of its associated
AcDbBlockTableRecord. This makes it easy to find the block table record in
which the layout’s actual geometry resides. If an AcDbBlockTableRecord rep-
resents a layout, then it contains the object ID of its associated AcDbLayout
object.
Most of the plot information for layout objects is stored in
AcDbPlotSettings, the base class of AcDbLayout. You can create named plot
settings and use them to initialize other AcDbLayout objects. This allows you
to export and import plot settings from one layout to another. These named
plot settings are stored in instances of the AcDbPlotSettings class. There is
one AcDbPlotSettings object for each named plot setting and they are stored
in the ACAD_PLOTSETTINGS dictionary within the named object
dictionary.
■ Create layouts
■ Delete layouts
■ Rename layouts
■ Copy and clone layouts
There is one instance of a layout manager per application. The layout man-
ager always operates on the current layout.
Xrecords
Xrecords enable you to add arbitrary, application-specific data. Because they
are an alternative to defining your own object class, they are especially useful
to AutoLISP programmers. An xrecord is an instance of class AcDbxrecord,
which is a subclass of AcDbObject. Xrecord state is defined as the contents of
a resbuf chain, which is a list of data groups, each of which in turn contains
a DXF group code plus associated data. The value of the group code defines
the associated data type. Group codes for xrecords are in the range of
1 through 369. The following section describes the available DXF group
codes.
There is no inherent size limit to the amount of data you can store in an
xrecord. Xrecords can be owned by any other object, including the extension
dictionary of any object, the named object dictionary, any other dictionary,
or other xrecords.
No notification is sent when an xrecord is modified. If an application needs
to know when an object owning an xrecord has been modified, the applica-
tion will need to send its own notification.
The AcDbXrecord class provides two member functions for setting and
obtaining resbuf chains, the setfromRbChain() and rbChain() functions:
Acad::ErrorStatus
AcDbXrecord::setFromRbChain(
resbuf& pRb,
AcDbDatabase* auxDb=NULL);
Acad::ErrorStatus
AcDbXrecord::rbChain(
resbuf** ppRb,
AcDbDatabase* auxDb=NULL) const;
The AcDbXrecord::setFromRbChain() function replaces the existing resbuf
chain with the chain passed in.
Xrecords | 161
DXF Group Codes for Xrecords
The following table lists the DXF group codes that can be used in xrecords.
1 4 Text
6 9 Text
38 59 Real
60 79 16-bit integer
90 99 32-bit integer
For a description of hard and soft owners and pointers, see chapter 12,
“Deriving from AcDbObject.”
Xrecords | 163
pXrec->setFromRbChain(*pHead);
acutRelRb(pHead);
pXrec->close();
}
printList(pRbList);
acutRelRb(pRbList);
}
165
166
MFC Topics
In This Chapter
8
The Microsoft Foundation Class (MFC) library allows ■ Introduction
■ Using MFC with ObjectARX
a developer to implement standard user interfaces
Applications
ObjectARX application.
167
Introduction
ObjectARX applications can be created to take advantage of the Microsoft
Foundation Class (MFC) library. This chapter discusses how to build your
ObjectARX applications to make use of MFC and how the AutoCAD built-in
MFC system can be used to create dialogs that behave and operate like
AutoCAD.
For complete information about MFC, see the Microsoft online help and
technical notes. In particular, see notes 11 and 33 for information about
using MFC as part of a DLL, which is an important concept for ObjectARX.
CAcExtensionModule Class
The ObjectARX SDK provides two simple C++ classes that can be used to
make resource management easier. The CAcExtensionModule class serves two
purposes—it provides a placeholder for an AFX_EXTENSION_MODULE structure
(normally used to initialize or terminate an MFC extension DLL) and tracks
two resource providers for the DLL. The resource providers are the module’s
resources (which are normally the DLL itself, but may be set to some other
module) and the default resources (normally the host application, but are
actually the provider currently active when AttachInstance() is called).
CAcExtensionModule tracks these to simplify switching MFC resource lookup
between the default and the module’s. A DLL should create one instance of
this class and provide the implementation for the class.
AdUi and AcUi provide classes that implement the following features:
■ Dialog resizing
■ Dialog data persistency
■ Tabbed dialogs
Class Hierarchy
The following are the supported classes in the AdUi and AcUi libraries. There
are classes present in the header files that are not shown and are for internal
use only and are not supported for use with ObjectARX.
CAdUiTipWindow Class
CAdUiTipWindow is the basic AdUi tip window class. These objects handle
generic tip display and know when to automatically hide themselves (such
as detecting cursor movement, a brief time-out, or keyboard activity).
CAdUiTextTip Class
CAdUiTextTip specializes CAdUiTipWindow to display a TextTip.
CAdUiDrawTipText Class
CAdUiDrawTipText is used internally by the AdUi messaging system to inform
a control that a tip window needs repainting. The control has the option of
changing attributes of the tip window’s device context and drawing the text.
CAdUiBaseDialog Class
CAdUiBaseDialog provides basic support for tip windows (ToolTips and
TextTips) and the AdUi message handling system. It also supports context
CAdUiDialog Class
CAdUiDialog is a general purpose class that provides a set of member func-
tions allowing for resizable dialogs and data persistency.
CAdUiFileDialog
CAdUiFileDialog specializes CFileDialog much the same way as
CAdUiBaseDialog specializes CDialog. The class provides basic support for tip
windows (ToolTips and TextTips), context help and AdUi message handling
in a common file dialog. Unlike CAdUiBaseDialog, there is no built-in sup-
port for position and size persistency.
CAdUiTabMainDialog Class
CAdUiTabMainDialog represents the main container dialog in a tabbed dialog.
CAdUiTabMainDialog and CAdUiTabMainDialog are used in place of
CPropertySheet and CPropertyPage to construct tabbed dialogs.
CAdUiTabChildDialog Class
CAdUiTabChildDialog represents a tab in a tabbed dialog.
CAdUiTabMainDialog and CAdUiTabChildDialog are used in place of
CPropertySheet and CPropertyPage to construct tabbed dialogs. Each tab in
a tabbed dialog is a CAdUiTabChildDialog.
CAcUiDialog Class
CAcUiDialog is a general-purpose class that provides a set of member func-
tions allowing for resizable dialogs and data persistency in AutoCAD.
CAcUiTabMainDialog Class
CAcUiTabMainDialog represents the main container dialog in an AutoCAD
tabbed dialog. CAcUiTabMainDialog and CAcUiTabMainDialog are used in
place of CPropertySheet and CPropertyPage to construct tabbed dialogs in
AutoCAD.
CAcUiTabChildDialog Class
CAcUiTabChildDialog represents a tab in a tabbed dialog.
CAcUiTabMainDialog and CAcUiTabChildDialog are used in place of
CAcUiAlertDialog Class
CAdUiAlertDialog represents an alert dialog with three buttons. One button
is the CANCEL button and the other two button labels are set by the pro-
grammer. It is a general-purpose alert dialog.
CAcUiFileDialog Class
CAcUiFileDialog provides an AutoCAD-specific derivation of
CAdUiFileDialog.
CAdUiTabExtensionManager Class
CAdUiDialogManager is a class that manages adding and removing tabs from
a tabbed dialog that is extensible. If a dialog is tab extensible, an instance of
this class is found in the CAdUiTabMainDialog.
CAdUiTab Class
CAdUiTab encapsulates the MFC CTabCtrl and adds functionality to it. One
of these objects is found in the main dialog object.
CAdUiDockControlBar Class
The CAdUiDockControlBar class, part of a docking system, adds extended
capabilities to the MFC CControlBar class. The main feature provided is the
resizing of the control bars when docked. More than one control bar can be
docked together, each of them being able to be resized individually using
splitters created by the docking system. CAdUiDockControlBar also comes
with a gripper bar and a close button when docked. Control bars’ state can
be switched from docked to undocked or vice versa, by double-clicking on
the gripper when docked, or the title bar when undocked, or by dragging
them with the mouse. The docking system handles the persistency of the
control bars, preserving their position and state across sessions. Finally,
CAdUiDockControlBar provides a default context menu to control the bar
behavior, with a possibility for the developer to customize this menu.
CAdUiEdit Class
CAdUiEdit is derived from the CEdit class to provide edit box controls. This
class provides support for tip windows for truncated text items (TextTips).
This class takes bit flags to add desired validation behavior, based on the
following types of input: Numeric, String, Angular, and Symbol names. Gen-
erally you should use one of the classes derived from the AutoCAD-specific
class CAcUiComboBox, which adds a specific data type validation and persis-
tency to the control. These are CAcUiStringEdit, CAcUiSymbolEdit,
CAcUiNumericEdit, and CAcUiAngleEdit.
CAcUiEdit Class
CAcUiEdit provides an AutoCAD-specific derivation of CAdUiEdit.
CAcUiAngleEdit Class
CAcUiAngleEdit is derived from CAcUiEdit and provides a specialized con-
structor to ensure that the AC_ES_ANGLE style bit is always set in the style
mask. Objects of this class are intended for use in editing angular/rotational
data specific to AutoCAD settings.
CAcUiNumericEdit Class
CAcUiNumericEdit is derived from CAcUiEdit and provides a specialized con-
structor to ensure that the AC_ES_NUMERIC style bit is always set in the style
mask. Objects of this class are intended for use in editing numeric data (such
as distance) specific to AutoCAD settings.
CAcUiStringEdit Class
CAcUiStringEdit is derived from CAcUiEdit and provides a specialized con-
structor to ensure that the AC_ES_STRING style bit is always set in the style
mask. Any input is acceptable.
CAdUiListBox Class
CAdUiListBox specializes the MFC CListBox to provide a control that sup-
ports AdUi messaging. The class can be used anywhere a CListBox can be
used. Since it provides the additional container-side support for AdUi regis-
tered messages, it is convenient to use CAdUiBaseDialog (or a derived class)
with the CAdUiListBox (or a derived class) controls.
CAdUiListBox provides features that allow the class to be used to subclass a
list box included in a combo box. When used in concert with a
CAdUiComboBox, the list box is able to track the combo box and, in the case of
an owner-draw control, either delegate drawing to the combo box or provide
its own drawing routines.
CAdUiListCtrl Class
CAdUiListCtrl is derived from CListCtrl class to provide list controls. This
class provides support for tip windows for truncated text items (TextTips).
TextTips will appear for truncated header items for list controls in a report
view, and for individual truncated text items in columns in the body of a list
control. Owner-drawn controls are supported.
CAdUiHeaderCtrl
CAdUiHeaderCtrl specializes CHeaderCtrl. Most often, CAdUiHeaderCtrl rep-
resents the subclassed header contained in a list control (CAdUiListCtrl).
You do not need to subclass the header control to get TextTip support for col-
umn headers in a list control (provided automatically in CAdUiListCtrl).
CAdUiComboBox Class
CAdUiComboBox is derived from the CComboBox class to provide combo box
controls. This class provides support for tip windows for truncated text items
(TextTips), and data validation in the edit control. This class takes bit flags to
add desired validation behavior, based on the following types of input:
numeric, string, angular, and symbol names. Generally, you should use one
of the classes derived from the AutoCAD-specific class CAcUiComboBox, which
CAcUiAngleComboBox Class
The CAcUiAngleComboBox constructor automatically creates a
CAcUiAngleEdit to subclass the control’s edit box. This allows for validation
of angles specific to AutoCAD settings.
CAcUiNumericComboBox Class
The CAcUiAngleComboBox constructor automatically creates a
CAcUiNumericEdit to subclass the control’s edit box. This allows for valida-
tion of numbers specific to AutoCAD settings.
CAcUiStringComboBox Class
The CAcUiStringComboBox constructor automatically creates a
CAcUiStringEdit to subclass the control’s edit box. Any input is acceptable.
CAcUiSymbolComboBox Class
The CAcUiSymbolComboBox constructor automatically creates a
CAcUiSymbolEdit to subclass the control’s edit box. Valid AutoCAD symbol
names are acceptable input.
CAcUiMRUComboBox Class
CAcUiMRUComboBox inherits CAcUiComboBox and serves as the base class for
owner-draw combo boxes that implement an MRU list. Each item in the list
can contain a small image followed by some text. Each item also tracks a
CAcUiArrowHeadComboBox Class
CAcUiArrowHeadComboBox specializes CAcUiMRUComboBox for dimensioning
arrowhead selection. The control displays bitmaps representing the standard
AutoCAD dimensioning arrowhead styles, which are always present in the
list. By default no optional or additional items are present or added. The
cargo associated with each item is the AutoCAD index for the associated
stock arrowhead. When MRU items are added to the list, they are automati-
cally assigned a unique cargo value (which will be greater than the AutoCAD
index for a user-defined arrowhead style).
CAcUiColorComboBox Class
CAcUiColorComboBox specializes CAcUiMRUComboBox for color selection. The
control displays color swatches representing selections from AutoCAD’s pal-
ette. The stock items always present in the control reflect color numbers 1
through 7. Both optional items are used; Option1 displays “ByLayer” and
Option2 displays “ByBlock”. MRU items display “Color nnn,” where nnn is
the associated color number. The cargo associated with each item indicates
an AutoCAD color number (such as 1 to 255), “ByBlock” relates to 0, and
“ByLayer” corresponds to 256. The Other1 item is enabled and triggers the
AutoCAD Color Selection dialog. If Other2 is enabled it displays as
“Windows...” and by default triggers the Windows Color Selection Common
dialog. If the user selects an item from either of these dialogs the selection
appears in the MRU list and becomes the current item in the control.
CAcUiLineWeightComboBox Class
CAcUiLineWeightComboBox specializes CAcUiMRUComboBox for lineweight
selection. The control displays a small preview of the lineweights AutoCAD
supports, ranging from 0.05mm to 2.11mm, and includes “None” and
optionally “Default”. Both metric and imperial values are displayed, depend-
ing on the setting of the LWUNITS system variable. Both optional items are
used; Option1 displays “ByLayer” and Option2 displays “ByBlock”. Each
item maintains cargo that corresponds to the item’s AcDb::kLnWtxxx value.
CAcUiPlotStyleNamesComboBox Class
CAcUiPlotStyleNamesComboBox specializes CAcUiMRUComboBox for plot style
name selection. The MRU functionality of the combo is not used, and
“ByLayer”, “ByBlock”, and “Other...” items can be conditionally displayed. If
present, the “Other...” item can trigger either the Assign Plot Style dialog or
the Set Current Plot Style dialog.
CAcUiMRUListBox Class
CAcUiMRUListBox derives from CAcUiListBox. It is used by CAcUiMRUComboBox
to subclass the control’s list box (ComboLBox) and provide DrawTip support.
Advanced applications that use specialized MRU combo boxes may need to
derive special MRU list boxes to display DrawTips correctly.
CAdUiOwnerDrawButton Class
This class provides a basic owner-draw button. The class can be used any-
where a CButton can be used. When used in an AdUi-derived dialog (or a class
that supports AdUi messaging) CAdUiOwnerDrawButton automatically pro-
vides for the display of an AdUi tip window. The class also supports
drag and drop, Static and Tool Display, and PointedAt effects. In Tool Display
mode, the button appears flat and pops up when pointed at (such as when
the mouse moves over the button). Clicking the button makes it push down.
In Static Display mode, the button appears flat and behaves more like a static
control than a push button. The combination of enabling drag and drop and
Static Display is appropriate for creating sites that receive files via
drag and drop.
CAdUiBitmapButton Class
This class specializes CAdUiOwnerDrawButton to provide a button that dis-
plays a bitmap (the image is drawn transparently in the button). By default,
objects of this class automatically resize to fit the associated bitmap image.
CAdUiBitmapStatic Class
CAdUiBitmapStatic specializes CAdUiBitmapButton to provide a button that
enables Static Display by default. These controls act more like statics than
pushbuttons.
CAdUiDropSite Class
CAdUiDropSite specializes CAdUiBitmapStatic to provide a button that
enables drag and drop as well as Static Display. These controls can receive
files via drag and drop.
CAdUiToolButton Class
CAdUiToolButton specializes CAdUiBitmapButton to provide a button that
enables Tool Display by default. These controls appear more like toolbar but-
tons than regular pushbuttons.
CAcUiPickButton Class
CAcUiPickButton specializes CAcUiBitmapButton, which is a wrapper for the
class CAdUiBitmapButton. CAcUiPickButton provides a button that displays a
standard pick button bitmap.
CAcUiSelectButton Class
CAcUiSelectButton specializes CAcUiPickButton. It provides a button that
displays a standard selection button bitmap.
theArxDLL.DetachInstance();
}
// Entry point
//
extern "C" AcRx::AppRetCode acrxEntryPoint(
AcRx::AppMsgCode msg, void* appId)
{
switch( msg )
{
case AcRx::kInitAppMsg:
acrxDynamicLinker->unlockApplication(appId);
acrxDynamicLinker->registerAppMDIAware(appId);
initApp();
break;
case AcRx::kUnloadAppMsg:
unloadApp();
break;
case AcRx::kInitDialogMsg:
break;
default:
break;
}
return AcRx::kRetOK;
}
You will also need to add the ObjectARX libraries to the project file, change
the .dll extension to .arx, and modify the .def file with the proper exports.
Then you should be able to compile and load the application.
IDC_BUTTON_POINT
IDC_EDIT_XPT
IDC_EDIT_YPT
IDC_EDIT_ZPT
IDC_BUTTON_ANGLE
IDC_EDIT_ANGLE
IDC_COMBO_REGAPPS
IDC_LIST_BLOCKS
3 Make sure the resource IDs match this diagram or the remaining code will
not work.
7 Now open the AsdkAcUiDialogSample.h header file and change the derivation
of the new dialog class. It should be derived from CAcUiDialog:
class AsdkAcUiDialogSample : public CAcUiDialog
bool AsdkAcUiDialogSample::ValidatePoint()
{
if (!m_ctrlXPtEdit.Validate())
return false;
if (!m_ctrlYPtEdit.Validate())
return false;
if (!m_ctrlZPtEdit.Validate())
return false;
return true;
}
bool AsdkAcUiDialogSample::ValidateAngle()
{
if (!m_ctrlAngleEdit.Validate())
return false;
return true;
}
2 Now add some utility functions to iterate over two symbol tables and display
the names in the two different list boxes:
void AsdkAcUiDialogSample::DisplayBlocks()
{
AcDbBlockTable *pBlockTable;
acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pBlockTable, AcDb::kForRead);
void AsdkAcUiDialogSample::DisplayRegApps()
{
AcDbRegAppTable *pRegAppTable;
acdbHostApplicationServices()->workingDatabase()
->getSymbolTable(pRegAppTable, AcDb::kForRead);
// Get a point
//
void AsdkAcUiDialogSample::OnButtonAngle()
{
// Hide the dialog and give control to the editor
//
BeginEditorCommand();
double angle;
void AsdkAcUiDialogSample::OnKillfocusEditXpt()
{
// Get and update text the user typed in.
//
m_ctrlXPtEdit.Convert();
m_ctrlXPtEdit.GetWindowText(m_strXPt);
}
void AsdkAcUiDialogSample::OnKillfocusEditYpt()
{
// Get and update text the user typed in.
//
m_ctrlYPtEdit.Convert();
m_ctrlYPtEdit.GetWindowText(m_strYPt);
}
void AsdkAcUiDialogSample::OnKillfocusEditZpt()
{
// Get and update text the user typed in.
//
m_ctrlZPtEdit.Convert();
m_ctrlZPtEdit.GetWindowText(m_strZPt);
}
6 The combo box handler allows the user to type in a string and then register
this as an application name. This doesn’t really make sense for an applica-
tion, but it shows the use of a combo box:
void AsdkAcUiDialogSample::OnKillfocusComboRegapps()
{
CString strFromEdit;
m_ctrlRegAppComboBox.GetWindowText(strFromEdit);
if (m_ctrlRegAppComboBox.FindString(-1, strFromEdit) == CB_ERR)
if (acdbRegApp(strFromEdit) == RTNORM)
m_ctrlRegAppComboBox.AddString(strFromEdit);
}
if (!ValidateAngle()) {
AfxMessageBox("Sorry, Angle out of desired range.”);
m_ctrlAngleEdit.SetFocus();
return;
}
DLGCTLINFOdlgSizeInfo[]= {
{ IDC_STATIC_GROUP1, ELASTICX, 20 },
{ IDC_STATIC_GROUP1, ELASTICY, 100 },
{ IDC_EDIT_XPT,ELASTICX, 20 },
{ IDC_EDIT_YPT,ELASTICX, 20 },
{ IDC_EDIT_ZPT,ELASTICX, 20 },
{ IDC_EDIT_ANGLE, ELASTICX, 20 },
{ IDC_STATIC_GROUP2, MOVEX, 20 },
{ IDC_STATIC_GROUP2, ELASTICY, 100 },
{ IDC_STATIC_GROUP2, ELASTICX, 80 },
{ IDC_LIST_BLOCKS, MOVEX, 20 },
{ IDC_LIST_BLOCKS, ELASTICY, 100 },
{ IDC_STATIC_TEXT2,MOVEX, 20 },
{ IDC_STATIC_TEXT2,MOVEY, 100 },
{ IDC_LIST_BLOCKS, ELASTICX, 80 },
{ IDC_STATIC_TEXT2,ELASTICX, 80 },
DisplayPoint();
DisplayAngle();
DisplayBlocks();
DisplayRegApps();
return TRUE; // return TRUE unless you set the focus to a control
}
In This Chapter
9
The global functions described in this chapter handle ■ Selection Set and Entity Names
■ Handling Selection Sets
selection sets, drawing entities, and symbol tables. See
■ Entity Name and Data Functions
the AutoCAD Customization Guide for background ■ Symbol Table Access
199
Selection Set and Entity Names
Most of the ObjectARX functions that handle selection sets and entities iden-
tify a set or entity by its name, which is a pair of longs assigned and main-
tained by AutoCAD. In ObjectARX, names of selection sets and entities have
the corresponding type ads_name.
Before it can manipulate a selection set or an entity, an ObjectARX applica-
tion must obtain the current name of the set or entity by calling one of the
library functions that returns a selection set or entity name.
NOTE Selection set and entity names are volatile; they apply only while you
are working on a drawing with AutoCAD, and they are lost when exiting from
AutoCAD or switching to another drawing.
For selection sets, which also apply only to the current session, the volatility
of names poses no problem, but for entities, which are saved in the drawing
database, it does. An application that must refer at different times to the same
entities in the same drawing (or drawings), can use entity handles, described
in “Entity Handles and Their Uses” on page 216.
int
acedSSGet (
const char *str,
const void *pt1,
const void *pt2,
const struct resbuf *entmask,
ads_name ss);
:$ Prompts supplied
. User pick
:? Other callbacks
A All
B Box
C Crossing
CP Crossing Polygon
:D Duplicates OK
:E Everything in aperture
F Fence
G Groups
I Implied
:K Keyword callbacks
L Last
M Multiple
P Previous
W Window
WP Window Polygon
NOTE AutoCAD cannot have more than 128 selection sets open at once. This
limit includes the selection sets open in all concurrently running ObjectARX
and AutoLISP applications. The limit may be different on your system. If the limit
is reached, AutoCAD refuses to create more selection sets. Simultaneously
managing a large number of selection sets is not recommended. Instead, keep
a reasonable number of sets open at any given time, and call acedSSFree() to
free unused selection sets as soon as possible. Unlike AutoLISP, the ObjectARX
environment has no automatic garbage collection to free selection sets after they
have been used. An application should always free its open selection sets when
it receives a kUnloadDwgMsg, kEndMsg, or kQuitMsg message.
NOTE If only filtering is specified (“X”) but the entmask argument is NULL,
acedSSGet() selects all entities in the database.
NOTE The resval specified in each buffer must be of the appropriate type.
For example, name types are strings (resval.rstring); elevation and thickness
are double-precision floating-point values (resval.rreal); color, attributes-
follow, and flag values are short integers (resval.rint); extrusion vectors are
three-dimensional points (resval.rpoint); and so forth.
// Select all the entities within the window that are also
// on the layer FLOOR9.
acedSSGet("W", pt1, pt2, &eb1, ssname1);
NOTE The meaning of certain group codes can differ from entity to entity, and
not all group codes are present in all entities. If a particular group code is speci-
fied in a filter, entities that do not contain that group code are excluded from
the selection sets that acedSSGet() returns.
Relational Tests
Unless you specify otherwise, there is an implied “equals” test between the
entity and each item in the filter list. For numeric groups (integers, real
values, points, and vectors), you can specify other relations by including rela-
tional operators in the filter list. Relational operators are passed as a special
-4 group, whose value is a string that indicates the test to be applied to the
next group in the filter list.
The following sample code selects all circles whose radii are greater than or
equal to 2.0:
eb3.restype = 40; // Radius
eb3.resval.rreal = 2.0;
eb3.rbnext = NULL;
Conditional Filtering
The relational operators just described are binary operators. You can also test
groups by creating nested Boolean expressions that use conditional opera-
tors. The conditional operators are also specified by -4 groups, but they must
be paired.
The following sample code selects all circles in the drawing with a radius of
1.0 and all lines on the layer “ABC”.
eb1 = acutBuildList(-4, "<or",-4, "<and", RTDXF0,
"CIRCLE", 40, 1.0, -4, "and>", -4, "<and", RTDXF0,
"LINE", 8, "ABC", -4, "and>", -4, "or>", 0);
NOTE Conditional expressions that test for extended data using the -3 group
can contain only -3 groups. See “Filtering for Extended Data” on page 206.
To select all circles that have extended data registered to either “APP1” or
“APP2” but not both, you could use the following code.
eb1 = acutBuildList(-4, "<xor", -3, "APP1", -3, "APP2",
-4, "xor>", 0);
NOTE The acedSSAdd() function can also be used to create a new selection
set, as shown in the following example. As with acedSSGet(), acedSSAdd()
creates a new selection set only if it returns RTNORM.
The following sample code fragment creates a selection set that includes the
first and last entities in the current drawing.
ads_name fname, lname; // Entity names
ads_name ourset; // Selection set name
NOTE Because selection sets can be quite large, the len argument returned
by acedSSLength() must be declared as a long integer. The i argument used
as an index in calls to acedSSName() must also be a long integer. (In this con-
text, standard C compilers will correctly convert a plain integer.)
Applying this matrix scales the entities by one-half (which moves them
toward the origin) and translates their location by (20.0,5.0).
int rc, i, j;
ads_point pt1, pt2;
ads_matrix matrix;
ads_name ssname;
rc = acedXformSS(ssname, matrix);
}
When you invoke acedDragGen(), you must specify a similar function to let
users interactively control the effect of the transformation. The function’s
declaration must have the following form:
int scnf(ads_point pt, ads_matrix mt)
It should return RTNORM if it modified the matrix, RTNONE if it did not, or
RTERROR if it detects an error.
The acedDragGen() function calls the scnf function every time the user
moves the cursor. The scnf() function sets the new value of the matrix mt.
When scnf() returns with a status of RTNORM, acedDragGen() applies the new
matrix to the selection set. If there is no need to modify the matrix (for exam-
ple, if scnf() simply displays transient vectors with acedGrVecs()), scnf()
should return RTNONE. In this case, acedDragGen() ignores mt and doesn’t
transform the selection set.
In the following example, the function sets the matrix to simply move (trans-
late) the selection set without scaling or rotation.
int dragsample(usrpt, matrix)
ads_point usrpt
ads_matrix matrix;
{
ident_init(matrix); // Initialize to identity.
// Initialize translation vector.
matrix[0][T] = usrpt[X];
matrix[1][T] = usrpt[Y];
matrix[2][T] = usrpt[Z];
The acdbEntLast() function retrieves the name of the last entity in the
database. The last entity is the most recently created main entity, so
acdbEntLast() can be called to obtain the name of an entity that has just
been created by means of a call to acedCommand(), acedCmd(), or
acdbEntMake().
strcpy(handle, "5a2");
NOTE Extended data can include entity handles to save relational structures
in a drawing. In some circumstances, these handles require translation or main-
tenance. See “Using Handles in Extended Data” on page 240.
Coordinate Transformation
The first of the additional arguments returned by acedNEntSelP() is a 4x4
transformation matrix of type ads_matrix. This matrix is known as the
Model to World Transformation Matrix. It enables the application to trans-
form points in the entity’s definition data (and extended data, if that is
present) from the entity’s model coordinate system (MCS) into the World
Coordinate System (WCS). The MCS applies only to nested entities. The ori-
gin of the MCS is the insert point of the block, and its orientation is that of
the UCS that was in effect when the block was created.
NOTE To transform a vector rather than a point, do not add the translation
vector [M03 M13 M23] (from the fourth column of the transformation matrix).
The following sample code defines a function, mcs2wcs(), that performs the
transformations described by the preceding equations. It takes the transfor-
mation matrix returned by acedNEntSelP() and a single point (presumably
from the definition data of a nested entity), and returns the translated point.
If the third argument to mcs2wcs(), is_pt, is set to 0 (FALSE), the last column
of the transformation matrix—the translation vector or displacement—is not
added to the result. This enables the function to translate a vector as well as
a point.
void mcs2wcs(xform, entpt, is_pt, worldpt)
ads_matrix xform;
ads_point entpt, worldpt;
int is_pt;
ads_point ownpoint;
M M M
00 10 20
M M M
01 11 21
X' Y' Z' 1.0 = X Y Z 1.0
M M M
02 12 22
M M M
03 13 23
Although the matrix format is different, the formulas are equivalent to those
for the ads_matrix type, and the only change required to adapt mcs2wcs()
for use with acedNEntSel() is to declare the matrix argument as an array of
four points.
void mcs2wcs(xform, entpt, is_pt, worldpt);
ads_point xform[4]; // 4x3 version
ads_point entpt, worldpt;
int is_pt;
The identity form of the 4x3 matrix is as follows:
100
010
001
000
refstkres
RTENAME RTENAME
ename1 ename2
outermost (inserted)
block that contains
the selected entity
containent[0] = containers->resval.rlname[0];
containent[1] = containers->resval.rlname[1];
rb = containers;
while (rb != NULL) {
prevrb = rb;
rb = containers->rbnext;
}
// The result buffer pointed to by prevrb now contains the
// name of the outermost block.
In the following example, the current coordinate system is the WCS. Using
AutoCAD, create a block named SQUARE consisting of four lines.
Command: line
From point: 1,1
To point: 3,1
To point: 3,3
To point: 1,3
To point: c
Command: block
Block name (or ?): square
Insertion base point: 2,2
Select objects: Select the four lines you just drew
Select objects: ENTER
Then insert the block in a UCS rotated 45 degrees about the Z axis.
Command: ucs
Origin/ZAxis/3point/Entity/View/X/Y/Z/Prev/Restore/Save/Del/?/
<World>: z
Rotation angle about Z axis <0>: 45
Command: insert
Block name (or ?): square
Insertion point: 7,0
X scale factor <1> / Corner / XYZ: ENTER
Y scale factor (default=X): ENTER
Rotation angle: ENTER
acdbEntLast(ent1);
ebuf = acdbEntGet(ent1);
eb = ebuf;
int printdxf(eb)
struct resbuf *eb;
{
int rt;
if (eb == NULL)
return RTNONE;
switch (rt) {
case RTSHORT:
acutPrintf("(%d . %d)\n", eb->restype,
eb->resval.rint);
break;
case RTREAL:
acutPrintf("(%d . %0.3f)\n", eb->restype,
eb->resval.rreal);
break;
case RTSTR:
acutPrintf("(%d . \"%s\")\n", eb->restype,
eb->resval.rstring);
break;
case RT3DPOINT:
acutPrintf("(%d . %0.3f %0.3f %0.3f)\n",
eb->restype,
eb->resval.rpoint[X], eb->resval.rpoint[Y],
eb->resval.rpoint[Z]);
break;
case RTNONE:
acutPrintf("(%d . Unknown type)\n", eb->restype);
break;
case -1:
case -2:
// First block entity
acutPrintf("(%d . <Entity name: %8lx>)\n",
eb->restype, eb->resval.rlname[0]);
}
return eb->restype;
}
In the next example, the following (default) conditions apply to the current
drawing.
Then a call to getlast() would print the following (the name value will
vary).
Results from acdbEntGet() of last entity:
(-1 . <Entity name: 60000014>)
(0 . "LINE")
(8 . "0")
(10 1.0 2.0 0.0)
(11 6.0 6.0 0.0)
(210 0.0 0.0 1.0)
NOTE The printdxf() function prints the output in the format of an AutoLISP
association list, but the items are stored in a linked list of result buffers.
The result buffer at the start of the list (with a -1 sentinel code) contains the
name of the entity that this list represents. The acdbEntMod() function uses
it to identify the entity to be modified.
The codes for the components of the entity (stored in the restype field) are
those used by DXF. As with DXF, the entity header items are returned only if
they have values other than the default. Unlike DXF, optional entity defini-
tion fields are returned regardless of whether they equal their defaults. This
simplifies processing; an application can always assume that these fields are
present. Also unlike DXF, associated X, Y, and Z coordinates are returned as
a single point variable (resval.rpoint), not as separate X (10), Y (20), and Z
(30) groups. The restype value contains the group number of the X coordi-
nate (in the range 10–19).
return rchain;
}
If the DXF group code specified by the gcode argument is not present in the
list (or if gcode is not a valid DXF group), entitem() “falls off the end” and
returns NULL. Note that entitem() is equivalent to the AutoLISP function
(assoc).
The acdbEntMod() function modifies an entity. It passes a list that has the
same format as a list returned by acdbEntGet(), but with some of the entity
group values (presumably) modified by the application. This function
complements acdbEntGet(); the primary means by which an ObjectARX
application updates the database is by retrieving an entity with
acdbEntGet(), modifying its entity list, and then passing the list back to the
database with acdbEntMod().
The following code fragment retrieves the definition data of the first entity
in the drawing, and changes its layer property to MYLAYER.
ads_name en;
struct resbuf *ed, *cb;
char *nl = "MYLAYER";
strcpy(cb->resval.rstring, nl);
if (acdbEntMod(ed) != RTNORM) {
acutRelRb(ed);
return BAD; // Error
}
break; // From the for loop
}
acutRelRb(ed); // Release result buffer.
Memory management is the responsibility of an ObjectARX application.
Code in the example ensures that the string buffer is the correct size, and it
releases the result buffer returned by acdbEntGet() (and passed to
acdbEntMod()) once the operation is completed, whether or not the call to
acdbEntMod() succeeds.
An application can also add an entity to the drawing database by calling the
acdbEntMake() function. Like acdbEntMod(), the argument to
acdbEntMake() is a result-buffer list whose format is similar to that of a list
returned by acdbEntGet(). (The acdbEntMake() call ignores the entity name
field [-1] if that is present.) The new entity is appended to the drawing data-
base (it becomes the last entity in the drawing). If the entity is a complex
entity (a polyline or block), it is not appended to the database until it is
complete.
The following sample code fragment creates a circle on the layer MYLAYER.
int status;
struct resbuf *entlist;
ads_point center = {5.0, 7.0, 0.0};
char *layer = "MYLAYER";
status = acdbEntMake(entlist);
acutRelRb(entlist); // Release acdbEntMake buffer.
if (status == RTERROR) {
acdbFail("Unable to make circle entity\n");
return BAD;
}
Both acdbEntMod() and acdbEntMake() perform the same consistency checks
on the entity data passed to them as the AutoCAD DXFIN command performs
when reading DXF files. They fail if they cannot create valid drawing entities.
Complex Entities
A complex entity (a polyline or block) must be created by multiple calls to
acdbEntMake(), using a separate call for each subentity. When
acdbEntMake() first receives an initial component for a complex entity, it
creates a temporary file in which to gather the definition data (and extended
data, if present). Each subsequent acdbEntMake() call appends the new sub-
entity to the file. When the definition of the complex entity is complete (that
is, when acdbEntMake() receives an appropriate Seqend or Endblk subentity),
the entity is checked for consistency, and if valid, it is added to the drawing.
The file is deleted when the complex entity is complete or when its creation
is canceled.
The following example contains five calls to acdbEntMake() that create a sin-
gle complex entity, a polyline. The polyline has a linetype of DASHED and a
color of BLUE. It has three vertices located at coordinates (1,1,0), (4,6,0), and
(3,2,0). All other optional definition data assume default values.
int status;
struct resbuf *entlist, result;
ads_point newpt;
entlist = acutBuildList(
RTDXF0, "POLYLINE",// Entity type
62, 5, // Color (blue)
6, "dashed",// Linetype
66, 1, // Vertices follow.
0);
if (entlist == NULL) {
acdbFail("Unable to create result buffer list\n");
return BAD;
}
if (status != RTNORM) {
acutPrintf ("%d",status);
acedGetVar ("ERRNO", &result);
acutPrintf ("ERRNO == %d, result.resval.rint);
acdbFail("Unable to start polyline\n");
return BAD;
}
newpt[X] = 1.0;
newpt[Y] = 1.0;
newpt[Z] = 0.0; // The polyline is planar
entlist = acutBuildList(
RTDXF0, "VERTEX", // Entity type
62, 5, // Color (blue)
6, "dashed", // Linetype
10, newpt, // Start point
0);
if (entlist == NULL) {
acdbFail("Unable to create result buffer list\n");
return BAD;
}
status = acdbEntMake(entlist);
acutRelRb(entlist); // Release acdbEntMake() buffer.
if (status != RTNORM) {
acdbFail("Unable to add polyline vertex\n");
return BAD;
}
newpt[X] = 4.0;
newpt[Y] = 6.0;
entlist = acutBuildList(
RTDXF0, "VERTEX", // Entity type
62, 5, // Color (blue)
6, "dashed", // Linetype
10, newpt, // Second point
0);
if (entlist == NULL) {
acdbFail("Unable to create result buffer list\n");
return BAD;
}
status = acdbEntMake(entlist);
acutRelRb(entlist); // Release acdbEntMake() buffer.
newpt[X] = 3.0;
newpt[Y] = 2.0;
entlist = acutBuildList(
RTDXF0, "VERTEX", // Entity type
62, 5, // Color (blue)
6, "dashed", // Linetype
10, newpt, // Third point
0);
if (entlist == NULL) {
acdbFail("Unable to create result buffer list\n");
return BAD;
}
status = acdbEntMake(entlist);
acutRelRb(entlist); // Release acdbEntMake() buffer.
if (status != RTNORM) {
acdbFail("Unable to add polyline vertex\n");
return BAD;
}
entlist = acutBuildList(
RTDXF0, "SEQEND", // Sequence end
62, 5, // Color (blue)
6, "dashed", // Linetype
0);
if (entlist == NULL) {
acdbFail("Unable to create result buffer list\n");
return BAD;
}
status = acdbEntMake(entlist);
acutRelRb(entlist); // Release acdbEntMake() buffer.
if (status != RTNORM) {
acdbFail("Unable to complete polyline\n");
return BAD;
}
Creating a block is similar, except that when acdbEntMake() successfully
creates the Endblk entity, it returns a value of RTKWORD. You can verify the
name of the new block by a call to acedGetInput().
if (entlist == NULL) {
acdbFail("Unable to create result buffer list\n");
return BAD;
}
status = acdbEntMake(entlist);
acutRelRb(entlist); // Release acdbEntMake buffer.
if (status != RTNORM) {
acdbFail("Unable to start anonymous block\n");
return BAD;
}
if (entlist == NULL) {
acdbFail("Unable to create result buffer list\n");
return BAD;
}
if (status != RTKWORD) {
acdbFail("Unable to close anonymous block\n");
return BAD;
}
status = acedGetInput(newblkname);
if (status != RTNORM) {
acdbFail("Anonymous block not created\n");
return BAD;
}
To reference an anonymous block, create an insert entity with
acdbEntMake(). (You cannot pass an anonymous block to the INSERT
command.)
Continuing the previous example, the following code fragment inserts the
anonymous block at (0,0).
basept[X] = basept[Y] = basept[Z] = 0.0;
entlist = acutBuildList(
RTDXF0, "INSERT",
2, newblkname, // From acedGetInput
10, basept,
0 );
if (entlist == NULL) {
acdbFail("Unable to create result buffer list\n");
return BAD;
}
status = acdbEntMake(entlist);
acutRelRb(entlist); // Release acdbEntMake buffer.
if (status != RTNORM) {
acdbFail("Unable to insert anonymous block\n");
return BAD;
}
In the following example, the first entity in the current drawing is a polyline
with several vertices. The following code modifies the second vertex of the
polyline and then regenerates its screen image.
ads_name e1, e2;
struct resbuf *ed, *cb;
acdbEntNext(e1, e2);
head
-1 0 -3
NULL
1001 1001
"APPNAME1" "APPNAME2"
If you attempt to add a 1001 group but no other extended data to an existing
entity, the attempt is ignored. If you attempt to make an entity whose only
extended data group is a single 1001 group, the attempt fails.
Layer name 1003. Name of a layer associated with the extended data.
Database 1005. Handles of entities in the drawing database. Under
handle certain conditions, AutoCAD translates these.
3D point 1010. Three real values, contained in a point.
Real 1040. A real value.
Integer 1070. A 16-bit integer (signed or unsigned).
Long 1071. A 32-bit signed (long) integer. If the value that
appears in a 1071 group is a short integer or a real value,
it is converted to a long integer; if it is invalid (for
example, a string), it is converted to a long zero (0L).
NOTE If a 1001 group appears within a list, it is treated as a string and does
not begin a new application group.
Registering an Application
Application names are saved with the extended data of each entity that uses
them and in the APPID table. An application must register the name or names
it uses. In ObjectARX, this is done by a call to acdbRegApp(). The
acdbRegApp() function specifies a string to use as an application name. It
returns RTNORM if it can successfully add the name to APPID; otherwise, it
returns RTERROR. A result of RTERROR usually indicates that the name is
already in the symbol table. This is not an actual error condition but a
status = acdbEntMod(working_ent);
// Only extended data from registered applications still in the
// working_ent list are modified.
As the sample code shows, extended data retrieved by the acdbEntGetX()
function can be modified by a subsequent call to acdbEntMod(), just as
acdbEntMod() is used to modify normal definition data. (Extended data can
also be created by defining it in the entity list passed to acdbEntMake().)
Returning the extended data of only specifically requested applications
protects one application from damaging the data of another application. It
also controls the amount of memory that an application uses, and simplifies
the extended data processing that an application performs.
NOTE Because the strings passed with apps can include wild-card characters,
an application name of “*” will cause acdbEntGetX() to return all extended
data attached to an entity.
When an entity is placed in a block definition (by means of the BLOCK com-
mand), the entity within the block is assigned new handles. (If the original
entity is restored with OOPS, it retains its original handles.) The value of any
extended data handles remains unchanged. When a block is exploded (with
EXPLODE), extended data handles are translated, in a manner similar to the
way they are translated when drawings are combined. If the extended data
handle refers to an entity not within the block, it is unchanged; but if the
extended data handle refers to an entity within the block, it is assigned the
value of the new (exploded) entity’s handle.
WARNING! The xrecord object is designed in a way that will not offend
earlier versions of AutoCAD; however, the xrecord object will disappear when
creating a DXF file from a pre-Release 13c4 level of AutoCAD.
Xrecord objects are generic objects intended for use by ObjectARX and
AutoLISP applications. This class allows applications to create and store
arbitrary object structures of arbitrary result-buffer lists of non-graphical
information completely separate from entities. The root owner for all appli-
cation-defined objects is either the named object dictionary, which accepts
any AcDbObject type as an entry, including AcDbXrecord, or the extension
dictionary of any object.
Applications are expected to use unique entry names in the named object
dictionary. The logic of using a named object dictionary or extension dictio-
nary entry name is similar to that of a REGAPP name. In fact, REGAPP names
are perfect for use as entry names when appending application-defined
objects to the database or a particular object.
The use of xrecord objects represents a substantial streamlining with respect
to the current practice of assigning xdata to entities. Because an xrecord
object does not need to be linked with an entity, you no longer need to create
dummy entities (dummy entities were often used to provide more room for
xdata), or entities on frozen layers.
Applications are now able to do the following:
In the following example, the function getblock() retrieves the first block
(if any) in the current drawing, and calls the printdxf() function to display
that block’s contents in a list format.
void getblock()
{
struct resbuf *bl, *rb;
The first argument to acdbTblSearch() is a string that names a table, but the
second argument is a string that names a particular symbol in the table. If the
symbol is found, acdbTblSearch() returns its data. This function has a third
argument, setnext, that can be used to coordinate operations with
acdbTblNext(). If setnext is zero, the acdbTblSearch() call has no effect on
acdbTblNext(), but if setnext is nonzero, the next call to acdbTblNext()
returns the table entry that follows the entry found by acdbTblSearch().
The setnext option is especially useful when dealing with the VPORT symbol
table, because all viewports in a particular viewport configuration have the
same name (such as *ACTIVE).
Keep in mind that if the VPORT symbol table is accessed when TILEMODE is
off, changes have no visible effect until TILEMODE is turned back on.
(TILEMODE is set either by the SETVAR command or by entering its name
directly.) Do not confuse the VPORT symbol table with viewport entities.
while (v != NULL} {
for (rb = v; rb != NULL; rb = rb->rbnext)
if (rb->restype == 2)
if (strcmp(rb->resval.rstring, "4VIEW") == 0) {
.// Process the VPORT entry
.
.
acutRelRb(v);
// Get the next table entry.
v = acdbTblNext("VPORT", 0);
} else {
acutRelRb(v);
v = NULL; // Break out of the while loop.
break; // Break out of the for loop.
}
}
In This Chapter
10
The global functions described in this chapter allow ■ AutoCAD Queries and
Commands
your application to communicate with AutoCAD. This
■ Getting User Input
245
AutoCAD Queries and Commands
The functions described in this section access AutoCAD commands and
services.
General Access
The most general of the functions that access AutoCAD are acedCommand()
and acedCmd(). Like the (command) function in AutoLISP, these functions
send commands and other input directly to the AutoCAD Command
prompt.
int
acedCommand(int rtype, ...);
int
acedCmd(struct resbuf *rbp);
Unlike most other AutoCAD interaction functions, acedCommand() has a
variable-length argument list: arguments to acedCommand() are treated as
pairs except for RTLE and RTLB, which are needed to pass a pick point. The
first of each argument pair identifies the result type of the argument that fol-
lows, and the second contains the actual data. The final argument in the list
is a single argument whose value is either 0 or RTNONE. Typically, the first
argument to acedCommand() is the type code RTSTR, and the second data argu-
ment is a string that is the name of the command to invoke. Succeeding
argument pairs specify options or data that the specified command requires.
The type codes in the acedCommand() argument list are result types.
The data arguments must correspond to the data types and values expected
by that command’s prompt sequence. These can be strings, real values,
integers, points, entity names, or selection set names. Data such as angles,
distances, and points can be passed either as strings (as the user might enter
them) or as the values themselves (that is, as integer, real, or point values).
An empty string (“”) is equivalent to entering a space on the keyboard.
Because of the type identifiers, the acedCommand() argument list is not the
same as the argument list for the AutoLISP (command) routine. Be aware of
this if you convert an AutoLISP routine into an ObjectARX application.
There are restrictions on the commands that acedCommand() can invoke,
which are comparable to the restrictions on the AutoLISP (command)
function.
return GOOD;
}
Provided that AutoCAD is at the Command prompt when this function is
called, AutoCAD performs the following actions:
1 Draws a circle that passes through (3.0,3.0) and whose center is at (0.0,0.0).
2 Changes the current thickness to 1.0. Note that the first call to
acedCommand() passes the points as strings, while the second passes a short
integer. Either method is possible.
3 Draws another (extruded) circle whose center is at (1.0,1.0,3.0) and whose
radius is 4.5. This last call to acedCommand() uses a 3D point and a real
(double-precision floating-point) value. Note that points are passed by refer-
ence, because ads_point is an array type.
Using acedCmd()
The acedCmd() function is equivalent to acedCommand() but passes values to
AutoCAD in the form of a result-buffer list. This is useful in situations where
complex logic is involved in constructing a list of AutoCAD commands. The
acutBuildList() function is useful for constructing command lists.
acedCmd(cmdlist);
acutRelRb(cmdlist);
System Variables
A pair of functions, acedGetVar() and acedSetVar(), enable ObjectARX
applications to inspect and change the value of AutoCAD system variables.
These functions use a string to specify the variable name (in either uppercase
or lowercase), and a (single) result buffer for the type and value of the vari-
able. A result buffer is required in this case because the AutoCAD system
variables come in a variety of types: integers, real values, strings, 2D points,
and 3D points.
The following sample code fragment ensures that subsequent FILLET
commands use a radius of at least 1.
struct resbuf rb, rb1;
acedGetVar("FILLETRAD", &rb);
rb1.restype = RTREAL;
rb1.resval.rreal = 1.0;
if (rb.resval.rreal < 1.0)
if (acedSetVar("FILLETRAD", &rb1) != RTNORM)
return BAD; // Setvar failed.
In this example, the result buffer is allocated as an automatic variable when
it is declared in the application. The application does not have to explicitly
manage the buffer’s memory use as it does with dynamically allocated
buffers.
AutoLISP Symbols
The functions acedGetSym() and acedPutSym() let ObjectARX applications
inspect and change the value of AutoLISP variables.
In the first example, the user enters the following AutoLISP expressions:
Command: (setq testboole t)
T
Command: (setq teststr “HELLO, WORLD”)
“HELLO, WORLD”
Command: (setq sset1 (ssget))
<Selection set: 1>
Then the following sample code shows how acedGetSym() retrieves the new
values of the symbols.
struct resbuf *rb;
int rc;
long sslen;
rc = acedGetSym("testboole", &rb);
if (rc == RTNORM && rb->restype == RTT)
acutPrintf("TESTBOOLE is TRUE\n");
acutRelRb(rb);
rc = acedGetSym("teststr", &rb);
if (rc == RTNORM && rb->restype == RTSTR)
acutPrintf("TESTSTR is %s\n", rb->resval.rstring);
acutRelRb(rb);
rc = acedGetSym("sset1", &rb);
if (rc == RTNORM && rb->restype == RTPICKS) {
rc = acedSSLength(rb->resval.rlname, &sslen);
acutPrintf("SSET1 contains %lu entities\n", sslen);
}
acutRelRb(rb);
Conversely, acedPutSym() can create or change the binding of AutoLISP
symbols, as follows:
ads_point pt1;
pt1[X] = pt1[Y] = 1.4; pt1[Z] = 10.9923;
rc = acedPutSym("longlist", rb);
acedPrompt("LONGLIST has been created\n");
acutRelRb(rb);
To set an AutoLISP variable to nil, make the following assignment and func-
tion call:
rb->restype = RTNIL;
acedPutSym("var1", rb);
Users can retrieve these new values. (As shown in the example, your program
should notify users of any changes.)
TESTSTR has been reset.
LONGLIST has been created.
Command: !teststr
(“GREETINGS”)
Command: !longlist
((-1 “The combinations of the world” “are unstable by nature.” 100 (1.4 1.4
10.9923) (“He jests at scars” “that never felt a wound.”)))
File Search
The acedFindFile() function enables an application to search for a file of a
particular name. The application can specify the directory to search, or it can
use the current AutoCAD library path.
In the following sample code fragment, acedFindFile() searches for the
requested file name according to the AutoCAD library path.
char *refname = "refc.dwg";
char fullpath[100];
.
.
.
if (acedFindFile(refname, fullpath) != RTNORM) {
acutPrintf("Could not find file %s.\n", refname);
return BAD;
result->resval.rstring=NULL;
Object Snap
The acedOsnap() function finds a point by using one of the AutoCAD Object
Snap modes. The snap modes are specified in a string argument.
In the following example, the call to acedOsnap() looks for the midpoint of
a line near pt1.
acedOsnap(pt1, "midp", pt2);
The following call looks for either the midpoint or endpoint of a line, or the
center of an arc or circle—whichever is nearest pt1.
acedOsnap(pt1, "midp,endp,center", pt2);
The third argument (pt2 in the examples) is set to the snap point if one is
found. The acedOsnap() function returns RTNORM if a point is found.
rc = acedVports(&rb);
acedRetList(rb);
acutRelRb(rb);
For example, given a single-viewport configuration with TILEMODE turned
on, the preceding code may return the list shown in the following figure.
rb NULL
RTSHORT RTPOINT RTPOINT
1 0.0 30.0
0.0 30.0
Similarly, if four equal-sized viewports are located in the four corners of the
screen and TILEMODE is turned on, the preceding code may return the con-
figuration shown in the next figure.
rb
RTSHORT RTPOINT RTPOINT RTSHORT RTPOINT RTPOINT
5 0.5 1.0 2 0.5 1.0
0.0 0.5 0.5 1.0
NULL
RTSHORT RTPOINT RTPOINT RTSHORT RTPOINT RTPOINT
3 0.0 0.5 4 0.0 0.5
0.5 1.0 0.0 0.5
The current viewport’s descriptor is always first in the list. In the list shown
in the preceding figure, viewport number 5 is the current viewport.
Geometric Utilities
One group of functions enables applications to obtain geometric informa-
tion. The acutDistance() function finds the distance between two points,
acutAngle() finds the angle between a line and the X axis of the current UCS
(in the XY plane), and acutPolar() finds a point by means of polar coordi-
nates (relative to an initial point). Unlike most ObjectARX functions, these
NOTE Unlike acedOsnap(), the functions in this group simply calculate the
point, line, or angle values, and do not actually query the current drawing.
The following sample code fragment shows some simple calls to the geomet-
ric utility functions.
ads_point pt1, pt2;
ads_point base, endpt;
ads_real rads, length;
.
. // Initialize pt1 and pt2.
.
// Return the angle in the XY plane of the current UCS, in radians.
rads = acutAngle(pt1, pt2);
// Return distance in 3D space.
length = acutDistance(pt1, pt2);
base[X] = 1.0; base[Y] = 7.0; base[Z] = 0.0;
acutPolar(base, rads, length, endpt);
The call to acutPolar() sets endpt to a point that is the same distance from
(1,7) as pt1 is from pt2, and that is at the same angle from the X axis as the
angle between pt1 and pt2.
pt1
The next figure shows the point values that acedTextBox() returns for sam-
ples of vertical and aligned text. In both samples, the height of the letters was
entered as 1.0. (For the rotated text, this height is scaled to fit the alignment
points.)
pt2 = 9.21954,1.38293
(10,3)
(1,1)
pt1 pt1 = 0,0 alignment points entered where text was created
= -0.5,-20.0
Note that with vertical text styles, the points are still returned in left-to-right,
bottom-to-top order, so the first point list contains negative offsets from the
text origin.
The acedTextBox() function can also measure strings in attdef and attrib
entities. For an attdef, acedTextBox() measures the tag string (group 2); for
an attrib entity, it measures the current value (group 1).
The following function, which uses some entity handling functions, prompts
the user to select a text entity, and then draws a bounding box around the
text from the coordinates returned by acedTextBox().
NOTE The sample tbox() function works correctly only if you are currently in
the World Coordinate System (WCS). If you are not, the code should convert the
ECS points retrieved from the entity into the UCS coordinates used by
acedCommand(). See “Coordinate System Transformations” on page 271.
User-Input Functions
The user-input or acedGetxxx() functions pause for the user to enter data of
the indicated type, and return the value in a result argument. The application
can specify an optional prompt to display before the function pauses.
NOTE Several functions have similar names but are not part of the user-input
group: acedGetFunCode(), acedGetArgs(), acedGetVar(), and
acedGetInput().
Code Description
NOTE The acedDragGen() function indicates arbitrary input (if this has been
enabled by a prior acedInitGet() call) by returning RTSTR instead of RTKWORD.
Keyword Specifications
The optional kwl argument specifies a list of keywords that will be recognized
by the next user-input (acedGetxxx()) function call. The keyword value that
the user enters can be retrieved by a subsequent call to acedGetInput(). (The
keyword value will be available if the user-input function was
acedGetKword().) The meanings of the keywords and the action to perform
for each is the responsibility of the ObjectARX application.
The acedGetInput() function always returns the keyword as it appears in the
kwl argument, with the same capitalization (but not with the optional char-
acters, if those are specified after a comma). Regardless of how the user enters
User Breaks
The user-input functions and the acedCommand(), acedCmd(), acedEntSel(),
acedNEntSelP(), acedNEntSel(), acedDragGen(), and acedSSGet() func-
tions return RTCAN if the AutoCAD user responds by pressing ESC . An external
function should treat this response as a cancel request and return immedi-
ately. ObjectARX also provides a function, acedUsrBrk(), that explicitly
checks whether the user pressed ESC . This function enables ObjectARX appli-
cations to check for a user interrupt.
An application doesn’t need to call acedUsrBrk() unless it performs lengthy
computation between interactions with the user. The function acedUsrBrk()
should never be used as a substitute for checking the value returned by user-
input functions that can return RTCAN.
In some cases, an application will want to ignore the user’s cancellation
request. If this is the case, it should call acedUsrBrk() to clear the request;
otherwise, the ESC will still be outstanding and will cause the next user-input
call to fail. (If an application ignores the ESC , it should print a message to tell
the user it is doing so.) Whenever an ObjectARX application is invoked, the
ESC condition is automatically cleared.
For example, the following code fragment fails if the user enters ESC at the
prompt.
int test()
{
int i;
while (!acedUsrBrk()) {
acedGetInt("\nInput integer:", &i); // WRONG
.
.
.
}
}