CP11-4   Getting Started with ObjectARX
Tom Stoeckel
         ObjectARX® is the most versatile and powerful programming
         interface for developing new commands and applications to customize
         and extend AutoCAD®. This course will introduce you to the basics of
         ObjectARX and its classes. It will provide an overview of what
         ObjectARX is all about, how it works, some of things you can expect
         to do with it and what you'll need to get started.
                                                                                1
Getting Started with ObjectARX
 Your instructor – Tom Stoeckel
  • Autodesk programmer for 6 years
  • Express Tools and AutoCAD
  • Battman, Layer Translator
  • Digital Signatures, Reference Manager
  • Customizing AutoCAD since Release 9
Getting Started with ObjectARX
 This is an INTRODUCTION to ObjectARX
 Some knowledge of C++ is assumed
 A chance for info later in the week
  • CP43-1 – December 5th @ 11:30am
  • Beginning ARX for the LISP and VBA programmer
  • Randy Kintzley
                                                    2
Housekeeping
 Class Materials
 Breaks
 Questions
 Session Evaluation Form
My Objective
You should know …
 • Some basics about ObjectARX
 • How to setup and use the ObjectARX SDK
 • Where to find help and reference materials
 • How to setup and use your compiler
 • How to create an ObjectARX project
 • Q&A
                                                3
What is ObjectARX?
ARX (AutoCAD Runtime eXtension)
 • A set of object oriented C++ libraries
 • Comprehensive API
 • Hundreds of classes
 • Thousands of unique member functions
Advantages to ObjectARX
 New objects can be added to AutoCAD and existing
 programs can work with them right away!
 Performance - Speed of execution
 Notification of events
 Provides a deep level of access into the system - the
 same as used by Autodesk themselves
 Database, operator interface, events
                                                         4
Downloading the ObjectARX SDK
Free for downloading
 • www.autodesk.com/developautocad
 • www.autodesk.com/objectarx
 • www.objectarx.com
Reference Materials
ObjectARX SDK Help
Autodesk Developer Network (ADN)
 • Free - www.autodesk.com/developautocad
 • Membership - www.autodesk.com/joinadn
        • Access to lots of Autodesk software
        • Beta releases (NDA)
        • Direct e-mail support
        • On-line access to samples and solutions
        • Invitations to ADN Technical Conferences
        • Opportunities to attend API training classes
Newsgroups
 • news://discussion.autodesk.com/autodesk.autocad.objectarx
                                                               5
Compiler Requirements
If developing for AutoCAD 2004
 • Microsoft® Windows® XP (Professional or Home), Windows
   2000, or Windows NT® 4.0 (SP6a or later)
 • Microsoft Visual C++® version 7.0
Visual Studio.NET 2003 (VC7.1) is NOT supported for ObjectARX
development with AutoCAD 2004.
Since Microsoft no longer sells Visual Studio .NET 2002 (VC7.0)
directly, if you don’t already own this version, the alternative is to
purchase the Visual Studio .NET 2003, and downgrade to 2002.
Information on downgrading can be found at
http://msdn.microsoft.com/vstudio/previous/downgrade.aspx
Compiler Requirements
If developing for AutoCAD 2002, 2000i or 2000
 • Microsoft® Windows® NT 4.0 or Microsoft®
   Windows® 2000
 • Microsoft Visual C++® version 6.0
                                                                         6
Integrating the SDK into the compiler environment
Add the path to the SDK
include and library directories
to the list of Visual Studio
search folders.
Tools->Options menu
Projects->VC++ Directories
Primary ObjectARX Libraries
 AcRx
  • System level routines (initialization, linking, registration, etc)
 AcEd
  • Editor services (monitoring and notification)
  • New AutoCAD command (defining and registering)
 AcDb
  • Database definitions for all geometry and table objects
  • Query, manipulation and creation
 AcGi
  • Graphic interface, drawing entities
 AcGe
  • 2D and 3D geometry elements and geometry data manipulation
 AdUi/AcUi
  • MFC derived controls
                                                                         7
Linking Requirements
All ObjectARX applications must link with acad.lib and rxapi.lib
Most will also require acdb16.lib so just include it as well
Additional libs required for classes:
 • AcRx, AcDb and AcGi - None
 • AcEd - acedapi.lib
 • AcGe - acge16.lib
 • AdUi - adui16.lib
 • AcUi - adui16.lib and acui16.lib
 • COM - axdb16.lib
How ObjectARX works
 ObjectARX is supplied as a set of libraries.
 ObjectARX programs are DLLs called by AutoCAD.
 AutoCAD starts an ObjectARX program
  • when loading initially
  • when loaded by another ARX module
  • when loaded using the command ARXLOAD
                                                                   8
How ObjectARX works (cont’d)
 After loading,
  • AutoCAD calls a standard entry point
  • Sends a code that tells the module to initialize
  • Sends other codes as needed
     • new drawing load
     • AutoLISP subr request
     • drawing saved
ObjectARX
 Linking up with AutoCAD
  • ARX module is loaded
  • AutoCAD calls the “entry point”
     • acrxEntryPoint()
     • required in every ObjectARX program
 First call is to initialize the interface
  • kInitAppMsg
                                                       9
Messages
 kInitAppMsg - initialize
 kUnloadAppMsg - unload
 kLoadDwgMsg - new drawing loaded
 kUnloadDwgMsg - drawing unloaded
 KInvkSubrMsg - from AutoLISP
 KEndMsg - end
 kQuitMsg - quit
Messages
 The “k” names are constants set to integers.
 During initialization it is time to establish
 communication with AutoCAD.
  • AutoCAD may NOT be all the way up!
  • DO NOT run commands or expect entities to exist.
  • DO establish new commands and reactor linkages
    to your functions.
                                                       10
Messages
 The second message into your ARX module will be to
 inform you that a drawing is loaded.
 Now AutoCAD is ready to process commands, allow
 for entity linkages to reactors and so forth.
 Init message will be sent once, load message may be
 sent multiple times.
The AutoCAD Editor
The AcEd class has many services which include:
 • Command stack utilities
 • Event notification
 • AcEdJig (used for interactive dragging)
There are two ways to register commands in ARX
 • acedDefun()
 • acedRegCmds->addCommand()
                                                       11
Command Registration
acedDefun()
Registers your commands as a LISP function
Invoked using (command)
Register during kLoadDwgMsg request
Must be removed with acedUndef() during
kUnloadDwgMsg request
Command Registration (cont’d)
acedRegCmds->addCommand()
Registers your commands as “native” commands on
the AutoCAD command stack with all the other
AutoCAD commands
ARX registered commands can be invoked using LISP
(command) or acedCommand()
                                                    12
AcDbLibrary
An AutoCAD drawing file is a collection of objects
stored in a database
By default a new database contains
 • 9 symbol tables
 • Named object dictionary
 • 200+ header variables (not objects)
AcDb classes give you access to these objects
AcDb Functions
Get the current database for the AutoCAD edit session
 •   acdbHostApplicationServices()->workingDatabase()
Multiple databases can be loaded at one time with
 •   readDwgFile()
 • The  AutoCAD editor can only work with the file that
   it has opened
 • Since a handle is only unique for a single database,
   an object ID is automatically created for each object
   in the session
                                                           13
AcDbDatabase
Is a collection of objects
 • Each with a unique identifier that is guaranteed for the context
   of that drawing
 • Entities
         • Graphical objects
 • Objects
         • Non-graphical
 • A fixed set of symbol tables
         • Layer, Linetype, Block, etc.
 • Dictionaries
         • Named object dictionary, Groups, etc.
Object IDs
Handle
 • Object stored on disk
 • Never changes and is never reused in the same drawing
 • Use when the ID must persist between sessions
 • Slower than ObjectID because they are stored as strings and
   string comparison is slow
ObjectID
 • Assigned when the drawing is opened
 • Will be different from session to session
Object Pointer
 • Assigned when an object is opened in memory
 • Only valid while the object is open
                                                                      14
ObjectID
The system creates an object ID for each object in the
database when it is read into memory
With the object ID, you can get a pointer to the actual
database object and modify it
Always use an object ID if you can as they are faster
than handles
Automatically translated to handles on filing
operations
Identifying an Object
 There are several ways to determine what kind of object is
 open
  • desc
      • Returns the class descriptor
  • cast
      • Returns an object of the specified type or NULL
       AcDbEllipse* ellipseEntity = AcDbEllipse::cast(curEntity);
  •   isKindOf
        • Object belongs to or is derived from the specified class
       pObj->isKindOf(AcDbText::desc()
  •   isA()
        • Returns the class descriptor object of an object whose
          class is unknown
       pObj->isA() == AcDbViewTableRecord::desc()
                                                                     15
Working with Database Resident Objects
 Creating New DRO
  • Create an instance of the objects’ class
  • Modify its state using it’s member functions
  • Add it to the proper table or dictionary
  • Close or cancel the object
 Modify or Query a DRO
  • Open the object using the objectID
  • Modify or query the object using the objects member functions
  • Close or cancel the object
Creating a New Object
AcGePoint3d p1, p2;
p1(0) = 1.0; p1(1) = 2.0; p1(2) = 0.0;
p2(0) = 2.5; p2(1) = 4.25; p2(2) = 1.0;
AcDbLine *pLine = new AcDbLine(p1, p2);
AcDbBlockTable *pBTable;
acdbHostApplicationServices()->workingDatabase->getSymbolTable(pBTable,
   AcDB::kForRead);
AcDbBlockTableRecord *pBRec;
pBTable->getAt(ACDB_MODEL_SPACE, pBRec, AcDb::kForWrite);
pBTable->close();
AcDbObjectId lineID;
pBRec->appendAcDbEntity(lineID, pLine);
pBRec->close();
pLine->close();
                                                                          16
Opening Database Resident Objects
Get a pointer to the actual object with
acdbOpenObject()
 acdbOpenObject(pLayer, objIdCurLayer, AcDb::kForRead);
Specify which mode of access you want
 • kForRead - up to 256 at one time on one object
 • kForWrite – only 1 at a time per object
Open for read then upgradeOpen() for write
Object State Query/Manipulation
You can choose to open erased objects, the default is
false
Use the member functions to directly access object’s
state (data)
When looking for functions in header files or the help
files, don’t forget that some member functions will be
inherited from base classes
                                                          17
Ending Object Query/Manipulation
When Finished
• close()
• cancel()
Recording is automatic from open() to close()
Undo playback occurs automatically when cancel() is
called instead of close()
Use Smart Pointers (dbobjptr.h)
Basic Error Handling in ObjectARX
Always check your return values before proceeding
Do not assume anything
Acad::ErrorStatus
Check for NULL values
Use acadErrorStatusText()
Return useful error information from your own
functions
                                                      18
Memory Management - General
Close everything you open
Delete your iterators
Use Smart Pointers when possible
 • include “dbobjptr.h”
 • Takes some of the burden off of the developer to
   keep track of everything
Memory Management - Objects
The responsibility of memory management changes
during the life cycle of an object.
When you create an instance of an object with new, it
allocates memory from AutoCAD’s drawing database
heap.
If your application never adds the object to the
database YOU are responsible for freeing that memory
with delete.
If the object is added to the database AutoCAD is now
responsible for that memory and you should NOT
delete it. Doing so would very likely cause AutoCAD to
terminate.
                                                         19
Memory Management - Strings
char* strings returned by the SDK are always allocated
from the AutoCAD heap
These strings should be de-allocated with
acutDelString()
char* pName = NULL;
if (pLayout->getLayoutName(pName) == Acad::eOk)
  acutDelString(pName);
Memory Management - Strings
const char* strings returned by the SDK are the
responsibility of AutoCAD and should not be de-
allocated
const char* pName;
pLayout->getLayoutName(pName);
When in doubt, check the SDK help for the function in
question
                                                         20
Container Objects
Symbol tables
 • Fixed set, you can not add or delete (only purge)
 • Contains instances of symbol table records
        • Layers
        • Linetypes
        • Blocks
 • Dictionaries
        • Generic container for any type of object
        • Can create your own
Special Symbol Table
Block Table
 • All entities are owned by block table records
 • Has three default records
        • *MODEL_SPACE
        • *PAPER_SPACE0
        • *PAPER_SPACE1
 • Entities must be added one of these BTR’s to be
   visible in AutoCAD editor
                                                       21
AcDbDictionary Definition
Database resident object
Maintains a map between text strings and database
objects
Becomes the owner of the added objects
Attaches itself to the object as a persistent reactor so
the dictionary can be notified when the object is
erased
Iterating Through Object Containers
Objects that use iterators
 • Symbol tables
 • Block table records
 • Dictionaries
 • Polylines
 • Block references (inserts)
        • Only useful when attributes are present
                                                           22
Iterator Protocol
Iterators can return erased as well as unerased
object/entities
Iterators can start at the beginning or end of the
container and walk forward or backward
Step direction can be changed any time
Iterators also provide a “seek” function to find a
specific object (by objectID or by object address)
Iterator Protocol
Inconsistency of iterator protocol reflects differences
in underlying containers
Obtaining, using, deleting iterators
 • Use the newIterator() function (when it exists) to
   create an iterator object and get a pointer to it
 • Use the iterator member functions to walk the list of
   objects in the container
 • Delete the iterator object when finished
 • Close the container object once the iterator is
   deleted
                                                           23
Navigating Through Symbol Tables
AcDbSymbolTableIterator
 • AcDbLinetypeTableIterator
 • AcDbBlockTableIterator
      • AcDbBlockTableRecordIterator
 • AcDbLayerTableIterator
 • AcDbTextStyleIterator
Reacting to Events
 Events are when something happens
 Process controls are examples of event driven
 systems.
 Reacting to seemingly random events may seem
 complicated, but it really isn’t and it is common in
 many applications.
                                                        24
Reactor
 A reactor is something that reacts to a notification.
 ObjectARX provides a set of standard reactors that
 can be used.
 You can have any number of reactors for a given
 notification type.
 There are five reactor categories in ARX
Reactor types
 General database
 Specific object
 Command activities
 ObjectARX monitoring
 General purpose transaction
                                                         25
Notification Definition
Every object maintains a list of reactors itself
Transient Reactors
 • Commonly known as event notification, to be used when you
   want to monitor things that are happening during the edit
   session
Persistent Reactors
 • A way to make your object intelligent, your custom object can
   react to whatever you want
        • Other object being moved or copied
        • Environment changes
Transient vs. Persistent
 Transient Reactor
  • Is an instance of a class
  • Not saved from edit session to session
  • Not copied with the objects
 Persistent Reactor
  • Is an objectID of a database resident object
  • Can be derived from any type AcDbObject
  • Stay with the drawing
  • Copied when object is copied.
                                                                   26
Editor Reactors – Notification Functions
commandWillStart()
commandEnded()
commandCancelled()
beginSave()
saveComplete()
etc.
Database Reactors – Notification Functions
objectAppended()
objectModified()
objectErased()
objectUnappended()
objectReappended()
etc.
                                             27
Object Reactors – Notification Functions
copied()
openedForModify()
modified()
modifiedGraphics()
modifiedXData()
erased()
objectClosed()
Adding a Reactor
 Finding the name to use is often the hardest step!
 Three steps
  • Derive from the appropriate base class
  • Override member functions for notifications you
    wish to receive
  • Instantiate an object of your derived class
  • Call addReactor() to addPersistentReactor()
    member of the notifier object, passing it either a
    pointer to or an objectID of the reactor object
 No VETO power!
                                                         28
Removing a Reactor
Remove reactors by calling removeReactor() or
removePersistentReactor() function of the notifier
object, passing a pointer to the reactor object.
When your application unloads, it should remove all
transient reactors it planted
AutoCAD will take care of deleting persistent reactors
because they are database objects
Transient Reactor Details
The reactor object is directly called by the notifier
object
Can be added to/removed from watched object in any
open mode
Addition/removal not subject to UNDO
Transient reactor attachments are not copied when an
object is copied
                                                         29
Persistent Reactors Details
Persistent reactors can only be derived from AcDbObject and
classes derived from it (i.e. AcDbEntity, Custom Objects)
Reactor objects must be added o the database and given an
owner so they will persist
Can only be added to/removed from watched object when open
from write
This object must be added to the database and given an owner,
preferably a container object (i.e. dictionary)
When an object is copied, any persistent reactors attached to the
object are copied as well.
Notification Restrictions
While object is notifying it is in a read-only state
You are not able to modify it until the end of the
commit process which can be checked with
 • objectClosed()
 • transactionEnded()
Notification is a read-only affair: No Veto!
 • (but workarounds…)
                                                                    30
“Undoing” an Event
No veto so how do I “undo” a change that I’ve been
notified of?
 • Cache state at time of openedForModify()
 • When objectClosed() notification occurs, open
   object for write and change state back (watch out
   for infinite notification loop)
 • Or put reactors on editor for lispEnded and
   commandEnded. When one of these notifications
   comes through then open object and change it back
Custom Objects
 Why create a new object?
 • For specific applications
 • For specific needs in your programs
 • For the fun of it?
 New objects are complete class definitions
  • You must explain everything about them to
    AutoCAD!
                                                       31
Custom Objects
 There are three macros that are available in
 ObjectARX that make the creation of new objects
 easy.
  • ACRX_DXF_DEFINE_MEMBERS
  • ACRX_DEFINE_MEMBERS
  • ACRX_NO_CONS_DEFINE_MEMBERS
 The hard part is overriding all the functions
Derive from Existing Class
 There are three classes to derive from that are used
 most frequently
  • AcDbObject
  • AcDbEntity
  • AcDbCurve
                                                        32
Why not use existing objects?
 There is little you can do to improve these objects
 themselves
 They already work! You want to make new objects.
Save and Load
 Not only do you have to define how to manipulate
 and draw the objects you must also define how to
 save and load the object.
 File storage is important - this where you
  • Set up group codes for the data
  • Define how the object is drawn if your program is
     not available to control them
                                                        33
Save and Load
 dwgOutFields - get data from the properties and
 output them with group codes.
  • writeItem
 dwgInFields - get data from file and put them in the
 properties of the custom object.
  • readItem
Draw the Object
 WorldDraw - draw in world view
 ViewportDraw - draw in a view port
 saveAs - draw when saving to a file
  • This is what object will look like when your
    ObjectARX program is NOT loaded.
 AcGi function library is used to draw the objects
                                                        34
Proxy objects
 Proxies occur when your ObjectARX program is not
 loaded.
  • These used to be called Zombies - a much more
    descriptive term!
  • worldDraw or SaveAs is used to define what the
    object will look like - this information is saved in
    the DWG outside of your control.
Creating a simple ObjectARX project
From the File pull down menu of Visual C++ .NET,
select New and Project....
Click on the VC++ Projects node in the Project Types
tree on the New Project dialog that appears.
Select Win32 Project in the list of templates.
Enter the desired project name in the Name edit box.
Set the location to the folder where you want your
project to be stored, then click OK. This will invoke the
Win 32 Application Wizard dialog.
Select Application Settings tab on the wizard. Select
DLL for the Application type option.
Click on Finish to create the project.
                                                            35
Creating a new ObjectARX project
Right click on the project node in the Solution explorer and select
the Properties in the right-click menu. This brings up the property
pages dialog for the project.
In the Configuration drop-down list, select All Configurations.
This will ensure that the changes we make are applied to all the
configurations.
Select the node C/C++ -> General and set Detect 64-Bit
Portability Issues to No. Click Apply button to apply the changes.
Select the node C/C++ -> Code Generation. Select the item
Runtime Library in the list. Assign the property Multi-threaded
DLL (/MD), by selecting the property from the drop-down list.
Select the node Linker -> Input. In the Additional Dependencies
item, add the following libraries: "rxapi.lib acdb16.lib acge16.lib
acad.lib acedapi.lib"
Next, select the node Linker -> General. In the Output File item,
change the extension of the output file from ".dll" to ".arx".
Create the definition (.DEF) file
Used to provide linkage to AutoCAD’s ObjectARX handling system
From the Project pull down menu, select Add New Item
In the Add New Item dialog, select item Def File (.def).
Enter the name of your project in the Name edit box.
Click "Open"
Add the following information to an EXPORTS section in the new
file. All ObjectARX applications have to export at least two
functions
 EXPORTS
  acrxEntryPoint PRIVATE
  acrxGetApiVersion PRIVATE
                                                                      36
Header Files
 Include necessary header files
#include <aced.h>
#include <rxregsvc.h>
 Add header files as needed by ObjectARX classes and
 functions that you use in your application
 See your SDK help to determine which headers are
 needed
Add Init and Unload functions
 These will be referenced by acrxEntryPoint()
 initApp()
  • Called by AutoCAD when our application is loaded
 unloadApp()
  • Called when our application is unloaded.
                                                       37
Add Init and Unload functions
void initApp();
void unloadApp();
void initApp()
{
  // register a command with the AutoCAD command mechanism
  acedRegCmds->addCommand("HELLOWORLD_COMMANDS",
  "Hello",
  "Bonjour",
  ACRX_CMD_TRANSPARENT,
  helloWorld);
}
void unloadApp()
{
  acedRegCmds->removeGroup("HELLOWORLD_COMMANDS");
}
Add the main function body
 This function simply prints a message at the AutoCAD
 command line
void helloWorld();
void helloWorld()
{
  acutPrintf("\nHello World!");
}
                                                             38
 acrxEntryPoint()
    This is the main program module
    AutoCAD expects to find one in every ARX app
    Messages are sent directly to your application
    Set up and shut down control handled here
extern "C" AcRx::AppRetCode
acrxEntryPoint(AcRx::AppMsgCode msg, void* appId)
{
  switch(msg) {
    case AcRx::kInitAppMsg:
        // Allow application to be unloaded. Without this statement, AutoCAD will
        // not allow the application to be unloaded except on AutoCAD exit.
        //
        acrxUnlockApplication(appId);
        // Register application as MDI aware. Without this statement, AutoCAD
        // will switch to SDI mode when loading the application.
        //
        acrxRegisterAppMDIAware(appId);
        initApp();
        break;
    case AcRx::kUnloadAppMsg:
        unloadApp();
         break;
    case AcRx::kLoadDwgMsg:
        // initialize lisp functions here
        break;
    case AcRx::kUnloadDwgMsg:
        // remove lisp functions here
        break;
    }
    return AcRx::kRetOK;
}
                                                                                    39
Macros to simplify common operations
Consolidate several lines of code into one
Add these to a header file in your project.
Check if the Acad::ErrorStatus is eOk
 inline int
 EOK(Acad::ErrorStatus arg)
 {
   return arg == Acad::eOk;
 }
Usage
Acad::ErrorStatus es;
es = acdbOpenObject(pBlockRef, blkRefIds[i], AcDb::kForWrite);
if (!EOK(es))
  break;
Macros to simplify common operations
Same as above only display an alert dialog when things aren’t eOk
 #define EOKM(arg) \
   ((arg == Acad::eOk) ? true : Mutter(__FILE__, __LINE__, arg))
 inline bool
 Mutter (char *file, int line, Acad::ErrorStatus arg)
 {
   char buf[1024];
   wsprintf(buf, "!%s@%d: %s", file, line, acadErrorStatusText(arg));
   acedAlert(buf);
   return false;
 }
Usage
Acad::ErrorStatus es;
es = acdbOpenObject(pBlockRef, blkRefIds[i], AcDb::kForWrite);
if (!EOKM(es))
  break;
                                                                        40
Macros to simplify common operations
Get the working database without all that typing
 inline AcDbDatabase *
 CURDB()
 {
   return acdbHostApplicationServices()->workingDatabase();
 }
Usage:
AcDbBlockTablePointer pBlockTable(CURDB(), AcDb::kForRead);
You get the idea!
ObjectARX Summary
 It is not difficult to program in ObjectARX
 Opens a wide world of possibilities
 The next step...
                                                              41
The next step
 Learn C/C++?
 More information?
 Learn by doing
Thank You
 Turn in your session evaluation forms as you leave
 Email: tom.stoeckel@autodesk.com
                                                      42
43