This site was built to allow users to learn about ObjectARX® and AutoCAD® programming.
ObjectARX® and AutoCAD® are trademarks of Autodesk Inc.

Sunday, April 24, 2005

Class 11 - Custom ObjectARX Class

Introduction

In my personal opinion, the greatest feature of ObjectARX is the capability to develop your own objects and entities. This powerful feature will allow you to create complex applications and provide your users a unique experience using your software.

This feature is possible since ObjectARX beginning (officially at AutoCAD R13). Since Autodesk provided this feature, its own developers thought that they could develop their selves vertical solutions for the most interesting market areas. At that time, products like MAP, MCAD and ADT start to show up.

Today there are several vertical products based on AutoCAD made by Autodesk and much more developed by third-party companies.

How is this possible?

ObjectARX takes full advantage of C++ language features like inheritance, polymorphism and overriding among many others. This allows Autodesk to publish part of AutoCAD source code using a SDK like package with library and header files.

Beyond this point, ObjectARX exports some classes allowing you to derive from them and implement your own behavior taking advantage of all ready-to-use methods and overriding those ones you need.

This way, when your custom class is defined and implemented your application will be compiled and linked with AutoCAD native libraries and headers and will be possible to load your module (DLL) at runtime to use your own objects inside AutoCAD! Great hum?

When to use and when not to use custom objects

Even being a powerful feature of ObjectARX, custom objects are not the best solution for any kind of implementation. Sometimes is better to user another solution like XData or XRecords. This will totally depend on how much complex your application is and how much complex will be the way users will interact with your product.

You need to be sure when to use or not custom classes inside ObjectARX. Personally I perform some questions to myself that will help me to decide:

- My product's elements are simple or complex?- My elements are only complex in terms of non-graphical data or they will require complex graphical representation?- Do I need to protect my element's data when the drawing is out of my company?- Will my elements present a complex interaction with users very different from AutoCAD native entities?- Do I need to share common information among my elements?

These questions will really help you to decide or not to user custom classes.

How to use a custom objects?

The first step is to choose your base class. This will depend on what type of custom object are you willing to implement. Basically you need to choose if it will represent an entity or a data object. If it will be an entity you will need to derive it from AcDbEntity or other of its derived classes. In other hand, if it will not have graphical appearance, you will derive it from AcDbObject. There are some ObjectARX classes that does not allow you to derive from. Take a look at ObjectARX documentation for a complete list.

It is very important to you clearly understand these differences between AcDbEntity and AcDbObject. Remember that every AcDbEntity if also an AcDbObject but NOT ALL AcDbObject is an AcDbEntity. This is because AcDbEntity is derived from AcDbObject.

I really would like you to walk through the ObjectARX class hierarchy to locate yourself in that tree and see clearly what are we talking about. There is a DWG file, called classmap.dwg, inside your ObjectARX SDK folder called \classmap.

Runtime Identification

AutoCAD requires that every custom class has its own runtime type identification. This will be used by AutoCAD and by your own application. Basically you don't need to care about this because there are MACROS to do this job for you. The AcRxObject class is the responsible to perform this feature and exactly due that it is on the top of AcRx tree.

The runtime identification is made by some functions like the following:

desc(), a static member function that returns the class descriptor object of a particular (known) class.

cast(), a static member function that returns an object of the specified type, or NULL if the object is not of the required class (or a derived class).

isA() returns the class descriptor object of an object whose class is unknown.

These functions are pretty useful because they help you to get runtime important information from AutoCAD native objects and your own objects. A good example if when you have a pointer to an AcDbEntity and would like to know if it is an AcDbCircle or an AcDbLine. How? Just use the above functions to get the information or even try to cast the pointer.

To declare these functions you will need to use the following MACRO inside your class declaration:

ACRX_DECLARE_MEMBERS(CLASS_NAME);

To implement these functions, you will need to use one of the following MACROS:

ACRX_NO_CONS_DEFINE_MEMBERS (CLASS_NAME, PARENT_CLASS):Use for abstract classes and any other classes that should not be instantiated.

ACRX_CONS_DEFINE_MEMBERS (CLASS_NAME, PARENT_CLASS, VERNO):Use for transient classes that can be instantiated but are not written to file.

ACRX_DXF_DEFINE_MEMBERS (CLASS_NAME,PARENT_CLASS, DWG_VERSION, MAINTENANCE_VERSION, PROXY_FLAGS, DXF_NAME, APP):Use for classes that can be written to, or read from, DWG and DXF files.

Additionally, you will need to initialize and delete your custom class from ObjectARX runtime tree which can be done, during your application's kInitAppMsg and kUnloadAppMsg using the following functions implemented by the above MACROS:

// Inside kInitAppMsg function handlerMyClass::rxInit();// Call this only once for all of your custom classesacrxBuildClassHierarchy();

This will guarantee that when your application is loaded AutoCAD recognizes your class and when it was not present (unloaded) AutoCAD will transform your class instances into Proxy entities. Proxy entities are a binary package that protects and preserves your custom object during DWG roundtrip without your application.

36 comments
:

I am porting my application from DWGDirect lib to RealDWG lib and have a one problem which is releated for Custom entities (derived classes).

With DWGDirect library it possible to make PSEUDO database object (These object are overriding the original objects from which are derived).

IF you make your custom entity ExAcDbLine which you derived it from AcDbLine and register it for a Run TIme Ident., when you read DWG file, RealDWG library for LINE give you pointer to the EXAcDbLine, not to AcDbLine. This is possible to do with DWGDirect library (there are macros for do this:ODDB_PSEUDO_DECLARE_MEMBERSODDB_PSEUDO_DEFINE_MEMBERS) but in RealDWG there are no these macros for this thing.

"Pseudo classes (PSEUDO database objects) is method to substitute run-time class only to alter some behavior or to store additional run-time information. They are saved to file as their base class."

It is possible to have same thing in RealDWG as there is in DWGDirect library.

I've been using AutoCad for a number of years and would like to learn ObjectARX. Where can I get Microsoft Visual C.NET 2005(8.0), I did a google search and found Microsoft Visual C++ 2005 Redistributable Package (x86) (is this the same?) Are there any books (for beginners)that you could recommend?

i have a bunch of custom objects etc and i want to expose them through the autocad VBA api. how can i do this? my objective is to provide and easy means for others to use my objects alongside the autocad objects in a vba routine. thanks

Custom entities are still available only through C++ even on 2010 but there is a new concept called overrule which allow you to tweak the native entities through a .NET layer.

If you need/want to use custom entities but make your ARX portion in .NET you will need to create a .NET wrapper to allow access to your custom class from .NET. This in fact is done by AutoCAD itself because the major part of its core is made still in pure C++ and the classes are exposed through .NET wrappers.

Take a look at your ARXWizard and you will find a .NET Wrapper wizard to allow a easy creation of the custom class .NET wrappers.

I did the following:1. I created an ObjectDBX with Extension DLL, MFC Support, COM Server using ATL, Import Acad COM Interfaces2. I added a custom class poly1 derived from AcDbPolyline with automation support3. I added an ObjectDBX ATL COM Wrapper Object DBX classname=poly1 with Entity interface support, everything else is default.The wizard terminates with error: 'undefined' is Null or no Object.I use VS2005 and ObjectARXWizard 6.0.1.0.Can this be done manually.

Perhaps it might be an interesting idea to extend one of your labs with a COM wrapper.

I got it, but it was not easy. So I hope this will help other readers as well:

In my ObjectARX class explorer there is definitely no "Add a .NET wrapper".I enabled it via editing C:\Program files\Autodesk\ObjectARX Wizards for AutoCAD 2008\ArxAddInWiz\resources\1033\ArxAddinWiz.xmlAdd in ^ClassExplorer>^ContextMenu>^Project>" the following line:^Entry wizard="vc\vcaddclass\ObjectARX\ArxNETWrapper.vsz">Add a .NET Wrapper...^/Entry>You must restart VS that the change takes effect.

At first: you must create your ObjectDBX-project with ".NET mixed managed code support" otherwise you will get compilation errors.Now we can right-click on "Add a .NET wrapper".It is important to fill in the correct "Managed Base class". E.g. when your object derives from AcDbPolyline you must fill in Polyline.

You will still get compilation errors with GetImpObj. To resolve this you have to add to your StdAfx.h the line #include "yourclass.h".Now you can view your class in the object-explorer.

I created a separate wrapper DLL for .NET. If I open this DLL with Reflector everything looks fine, but if I add the DLL to a VB.NET project I only see the underlying Polyline object and none of my class properties. If you wish, I can send you the DLL or the project.

you are on the right track. string did not work, butBPipeline(System::String* sHatch)does. But how can I convert this to AcString ?APlant::BPipeline::BPipeline(System::String* sHatch) :Autodesk::AutoCAD::DatabaseServices::Polyline(new APipeline(*sHatch), true) {} ?The next headache will be my GapInsert function. I need to find an equivalent for AcGePoint3dArray&

1. String. I got it working the following way:a) In my dbx I changed APipeline(AcString sHatch) to APipeline(const wchar_t* sHatch)b) In my wrapper I wroteAPlant::BPipeline::BPipeline(System::String* sHatch) :Autodesk::AutoCAD::DatabaseServices::Polyline(new APipeline(StringToWchar::StringToWchar(sHatch)), true)StringToWchar is defined in mgdinterop.h

That's great!If you want, organize the information with the code fragments you built and I can publish it here with credits to you (send me your name, e-mail, etc). This way other people will find it easier.

as I see your blog cuts the lines so some information is missing.I want to test my solution a bit further to be quite sure that it is ok. At the moment some things simply work but I do not know why.

The motivation behind this is to reduce the quite unproductive and error-prone C++ to the minimum (i.e. the custom object DBX). I hope that I do not need C++ any longer for the ARX part.I also do not know whether the wrapper library will have performance impacts.

I could not find your email-adress. If you send it to lebakram1_at_gmx.net I will send you the routines which you can hopefully publish in a readable form.

When you derive from a class different of AcDbEntity I guess the OPM use the COM class descriptor to display a name at OPM dialog. If you don't implement the COM interface it will look up the next parent class information which is AcDbPolyline in your case.

This seems to be a specific behavior and I'm not sure you can avoid this by implementing some COM interface methods for your custom class. You need to try to see what happens.

Have you tried to compile and test PolySamp which derives from AcDbPolyline too but implements a COM interface? Take a look and see what OPM display for it.

you are probably right. I looked at polysamp and it displays the custom classname in the property palette. But polysamp has several thousand lines and I am not able to find out where the crucial information hides. Trying to adapt compoly to my custom object produced more than 100 errors and I have no idea what idl is & how it works. Probably it's not worth the effort.

I got a drawing with Custom Entities and also got the Object Enablers. So now the entities show up in the drawing. Now is it possible to read the properties of the Custom Entities through .Net API ? I also need to read the position of the Custom Entity and need to dimension them.

My best guess is, the developer of the Custom Entity need to take care about exposing the properties so that those properties can be accessed by .Net API. Am I correct ? Now I don't have anything excepts the object enablers ( dbx ). How to proceed ?

You can only read through .NET what was exposed through C++ classes via .NET wrappers.If the Custom Entity author didn't create a mixed-mode module exposing their classes and properties you won't be able to read from .NET anything beyond the AutoCAD base class level information. Thinks like Layer, Color, LineType, etc.

To hack DBX object enablers you would need to replicate all the class structure, their embedded objects, their properties and worse, figure out the sequence they were saved into the DWG once it is a binary sequential file.