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