ObjectARX & Dummies

Course Index

*Course support: Click here!
*Course and User samples: Download here!

Saturday, February 05, 2005

Class 2 - AutoCAD's Database

Introduction

Each AutoCAD drawing represents a structured Database which stores several types of objects. When you just open a new drawing, AutoCAD creates behind the scenes a organized and efficient Database. This Database has a minimun data that allows you to make basic drawings.

This minimun data is represented basically by objects like layers, linetypes, text styles, etc. Due that you have layer 0, stadard text style, continuous linetype and others.
Since AutoCAD Release 2000 you can work with multiple drawings at the same time which is called MDI environment. This functionality brings great flexbility but also bring some extra complexity when dealing with more than one drawing. We won't discuss MDI aspects on this course but this will be probably a requirement for your future ObjectARX applications.

How data is stored

This Database mantains all sort of objects a drawing needs to exist. These objects are stored into appropriate containers which are special objects made to manage objects of the same type. This way we have appropriate methods and procedures to store entities, layers, text styles, etc.

Each object stored into Database receives an identifier that is called ObjectId. This identifier is unique inside the same AutoCAD session and it is valid during the lifecycle of each object. The ObjectId is generated by its Database and you don't need to worry about how to create it.

Inside ObjectARX we have basically 3 kind of objects:
  • Entities: Objects with graphical representation (lines, arcs, texts, ...);
  • Containers: Special objects to store and manage collections (layer table, linetype table, ...);
  • Objects: Objects without any graphical representation (groups, layouts, ...).



AutoCAD's Database structure

Creating objects

To create an object through ObjectARX we have some kind of recipe depending on what type of object it is and where we would like to store it (most of time we need to store an objects inside its specific container). Basically, you will follow this sequence:

  1. Declare a pointer to the object type you would like to create and call its new operator;
  2. With this pointer, call appropriate methods of this object to change its features;
  3. Get a pointer to the Database where you would like to create the object (most of time the current Database);
  4. Open the appropriate container where it should be stored;
  5. Call the specific container method to store your object passing its pointer;
  6. Receive its ObjectId automatically generated by its container;
  7. Finish the operation closing all opened objects including containers and the object you have just created.

Obviously you will create some handy classes to allow the automation of this processes because they are very similar and can be easily reused. The main idea is to create a sort of database utility funcions like: AddLayer, AddLine, AddCircle, AddTextStyle, etc.

* It is very important to not forget to close opened objects because this will cause AutoCAD to terminate.

Sample code to create a line (AcDbLine)

This code demonstrates how to create a simple line between two points. The process is simple and no error check is made. This code needs to be embedded inside an ObjectARX application structure to work. The main idea is to show you the concepts. Further we will create a working code. Pay attention to the order of opening and closing operations.



// We first need to declare a couple of points
AcGePoint3d startPt(1.0, 1.0, 0.0);
AcGePoint3d endPt(10.0, 10.0, 0.0);

// Now we need to instantiate an AcDbLine pointer
// In this case, its constructor allows me to pass the 2 points

AcDbLine *pLine = new AcDbLine(startPt, endPt);

// Now we need to open the appropriate container which is inside BlockTable
AcDbBlockTable *pBlockTable = NULL;

// First, get the current database and then get the BlockTable
AcDbDatabase* pDB = acdbHostApplicationServices()->workingDatabase();
pDB->getSymbolTable(pBlockTable, AcDb::kForRead);

// Inside BlockTable, open the ModelSpace
AcDbBlockTableRecord* pBlockTableRecord = NULL;
pBlockTable->getAt(ACDB_MODEL_SPACE, pBlockTableRecord, AcDb::kForWrite);

// After get ModelSpace we can close the BlockTable
pBlockTable->close();

// Using ModelSpace pointer we can add our brand new line
AcDbObjectId lineId = AcDbObjectId::kNull;
pBlockTableRecord->appendAcDbEntity(lineId, pLine);

// To finish the process we need to close ModelSpace and the entity
pBlockTableRecord->close();
pLine->close();



On the next class we will present the ObjectARX application structure and will build and compile a simple application using the above code. See you there!

47 Comments:

  • Hello,
    Should I delete the AcDbEntity pointer?

    By Anonymous Anonymous, at 6:07 PM  

  • Why you don't close the AcDbDatabase pointer?

    By Anonymous Anonymous, at 6:09 PM  

  • Hi there!

    1)You only should delete those pointers you have allocate but not added to database. Once you have add an object to database AutoCAD take control of its lifecycle and you only need to close it.

    2)The AcDbDatabase pointer is managed by AutoCAD and it stay opened as long as its associated drawing is available. You should only delete AcDbDatabase pointer when you have allocate it and it is not attached to any opened document inside AutoCAD application environment.

    By Blogger Fernando Malard, at 7:29 PM  

  • Hi Fernando,

    Here is what I did base on your code sample.

    // ----- lesqsimpleLine._MyLine command (do not rename)
    static void lesqsimpleLine_MyLine(void)
    {

    acutPrintf("\nSimple Line Command in ObjectARX...");

    int iRet;

    AcGePoint3d startPt, endPt;

    iRet = acedGetPoint(NULL , "\nFrom point:" , asDblArray(startPt) );

    if (iRet == RTNORM)
    {

    iRet = acedGetPoint(asDblArray(startPt) , "\nTo point: " , asDblArray(endPt) );

    if (iRet == RTNORM)
    {
    AcDbLine *pLine = new AcDbLine(startPt, endPt);

    AcDbBlockTable *pBlockTable = NULL;

    AcDbDatabase* pDB = acdbHostApplicationServices()->workingDatabase();
    pDB->getSymbolTable(pBlockTable, AcDb::kForRead);

    AcDbBlockTableRecord* pBlockTableRecord = NULL;
    pBlockTable->getAt(ACDB_MODEL_SPACE, pBlockTableRecord, AcDb::kForWrite);

    pBlockTable->close();

    AcDbObjectId lineId = AcDbObjectId::kNull;
    pBlockTableRecord->appendAcDbEntity(lineId, pLine);

    pBlockTableRecord->close();
    pLine->close();

    }

    else { acutPrintf("\nNo second point selected..."); }

    }

    else { acutPrintf("\nNo first point selected..."); }

    By Anonymous Anonymous, at 5:49 PM  

  • Hi,

    I did not test your code but it looks good.

    Keep improving!

    BTW, I will request those users who would like to submit their improved samples and post into a user files folder.

    Regards,
    Fernando.

    By Blogger Fernando Malard, at 5:59 PM  

  • Great,

    Just let me know where that folder is going to be please.


    Gracias,
    Luis.

    By Anonymous Anonymous, at 6:10 PM  

  • Hi Fernando & Luis,

    There are code fragements designed by you two in this section. Can anybody tell me which environment this code fit? is it a Visual C++ one?

    Best Regards,

    Yang

    By Anonymous Anonymous, at 10:46 AM  

  • Hi Yang,

    Yes, these code fragments are in C++ targeting ObjectARX 2004 and above.

    You need to use VC++.NET 2002 to create ObjectARX applications compatible with AutoCAD 2004, 2005 and 2006.

    Regards,
    Fernando

    By Blogger Fernando Malard, at 7:10 PM  

  • is there any vc++ with objectarx free study material.

    By Blogger Rebekh, at 8:02 AM  

  • Hello Rebekh,

    Major ObjectARX contents on the Internet are placed into Forum Groups and Blogs. There are a couple of books about ObjectARX at Amazon.com

    Only free sources I known is my Blog and Kean's Blog (http://through-the-interface.typepad.com/). VC++ information can be found at Microsoft MSDN website.

    Hope this helps.
    Regards.

    By Blogger Fernando Malard, at 8:17 AM  

  • Thank u for ur help.

    By Blogger Rebekh, at 10:19 AM  

  • Hello,

    I build your Program in studio.net

    I obtain the following error

    "fatal error C1083: Cannot open include file: 'arxHeaders.h': No such file or directory "

    how cain i Proceed?

    By Blogger RadhaKrishna, at 4:50 PM  

  • Hello,

    I build your Program in studio.net

    I obtain the following error

    "fatal error C1083: Cannot open include file: 'arxHeaders.h': No such file or directory "

    how cain i Proceed?

    By Blogger RadhaKrishna, at 4:51 PM  

  • Hi,

    Take a look at Class3a. You need to setup VS to know both \inc and \lib ObjectARX paths.

    Regards,
    Fernando.

    By Blogger Fernando Malard, at 5:37 PM  

  • Hello

    How can I acess, add or modify from program, entries made in Drawing Properties... on the Custom tab?

    Regards

    By Anonymous Anonymous, at 9:40 AM  

  • Hi,

    There is a class to handle that. It is called AcDbDatabaseSummaryInfo and its pointer can be acquired with acdbGetSummaryInfo() global function.

    If you need to access the same information from outside AutoCAD, there is an ActiveX control called DwgPropsX. Inside this control you will find several methods to put/get information.

    Regards,
    Fernando.

    By Blogger Fernando Malard, at 10:51 AM  

  • Hello

    I have a class derived from AcDbDoubleClickEdit, and I have a class derived from AcDbEntity.

    I use ...desc()->addX(AcDbDoubleClickEdit::desc() ... to add the double click reactor to the entity.

    My goal is to modify some entity-s internal data in the database when the user double clicks on a specific entity.

    But I can't open them in write mode, i get error eLockViolation .
    Is there any way to overcome this obstacle?

    Regards, Alonso

    By Anonymous Alonso, at 7:42 AM  

  • Hi Alonso,

    If you are using AutoCAD 2007 the double click API is now part of CUI. Take a look at the .NET class:

    Autodesk.AutoCAD.Customization.DoubleClickAction

    If you are not using 2007, try to put the following code just before you open the entity for write:

    AcApDocument *pDoc=acDocManager->curDocument();
    acDocManager->lockDocument(pDoc,AcAp::kWrite);

    After close the entity, unlock the document like this:

    acDocManager->unlockDocument(pDoc);

    Hope this help.
    Regards.

    By Blogger Fernando Malard, at 9:06 AM  

  • Hello

    Thx for the help, it really solved my problem.

    Grateful, Alonso

    By Anonymous Alonso, at 4:31 AM  

  • Hello

    I have an arx application and a custom menu wrote for AutoCAD 2006.

    How can I load the custom menu from the application, without telling the user to use the AutoCAD command CUI?

    Can you give me an example how to use DEMANDLOAD to set my arx application to load whenever AutoCAD starts?

    I'm aiming to make an install kit for my program where I set everything, so the user simply starts AutoCAD and can use my menu and program.

    Regards, Alonso

    By Anonymous Alonso, at 5:52 AM  

  • Hi Alonso,

    The AutoCAD MENU and TOOLBARS can be manipulated directly through the COM API or through C# (.NET API). There is a sample at:

    C:\ObjectARX 2007\samples\dotNet\CuiSamp

    It shows how to create menus, toolbars, etc., using the .NET CUI API through C# (the same works if you use VB.NET).

    Regarding to the demand loading process it is just a matter of registry entries. You can find more information about this on the ObjectARX SDK Help into "Demand Loading" topic.

    Regards,
    Fernando.

    By Blogger Fernando Malard, at 8:20 AM  

  • please send simple code of vb to integrete autocad database
    to get the parameter of line

    By Blogger sandip, at 7:13 AM  

  • Hello Fernando,

    I have a question:

    [code]
    AcDbObjectId lineId=AcDbObjectId::kNull;

    pBlockTableRecord->appendAcDbEntity(lineId,pLine);

    [/code]

    Why don't you use function: appendAcDbEntity(pLine) ??

    Regards,
    Duy

    By Anonymous Duy, at 6:37 PM  

  • Hi Duy,

    In fact you are right but I prefer to use this other signature version which returns the new entity's objectId because would be a good idea to check if it is valid.

    I have removed a lot of code check on these samples to provide a clear code and an easy understanding.

    It is really up to you to choose the right signature to use.

    Regards,
    Fernando.

    By Blogger Fernando Malard, at 10:16 PM  

  • Dear Fernando,

    Please how to determine if DB pointer is still valid.

    In my case I have two DWG for example dwg1 and dwg2 when close dwg2 the pointer is invalid after close document so case terminate of program.

    So what is the solution?

    Is the solution using Reactor? Please give me any code sample if answer is yes?

    Best Regards,
    M.S

    By Anonymous Anonymous, at 10:44 AM  

  • Hi M.S,

    It is not a good idea to keep the pointers of databases. The best approach is to monitor the documents through a document reactor and then get each document attached database pointer.

    Take a look at the docman sample of ObjectARX SDK.

    Regards,
    Fernando.

    By Blogger Fernando Malard, at 10:56 AM  

  • Dear Fernando,

    I have this code it work but when replace activateDocument with setCurDocument at line 12 and 16 this code does not work (Terminate program)
    (This code activate second dwg if first dwg is active and vise versa)

    AcDbVoidPtrArray *m_pDBArray = acdbActiveDatabaseArray();
    if(m_pDBArray->length() == 2)
    {
    AcDbDatabase *pDb1 = (AcDbDatabase *)(m_pDBArray->at(0));
    AcDbDatabase *pDb2 = (AcDbDatabase *)(m_pDBArray->at(1));

    AcApDocument *pDoc0;
    AcApDocManager *doc_manager = acDocManagerPtr();
    pDoc0 = doc_manager->curDocument();

    AcDbDatabase *pDbTemp0 = pDoc0->database();
    If(pDbTemp0 == pDb1)
    {
    AcApDocument *pDoc;
    pDoc = doc_manager->document(pDb2);
    Acad::ErrorStatus error11= doc_manager->activateDocument(pDoc);
    }
    else if(pDbTemp0 == pDb2)
    {
    AcApDocument *pDoc;
    pDoc = doc_manager->document(pDb1);
    Acad::ErrorStatus error11= doc_manager->activateDocument(pDoc);
    }
    }
    else
    {
    acutPrintf("\n Number of DWG less than 2");
    }

    Do you have any idea?

    Regards,
    M.S

    By Anonymous Anonymous, at 3:06 AM  

  • Hi M.S,

    Activate a document and make a document current are not the same thing. I would recommend you to read the ObjectARX documentation on the MDI section which explains in details what each concept is.

    There are some restrictions that are valid to activate a document and others to set a document current.

    Regards,
    Fernando.

    By Blogger Fernando Malard, at 10:38 AM  

  • Hi,

    I have plane AutoCad 2008 and AutoCad Map 2009 on the same machine.

    Arx's with the line

    AcDbDatabase* pDB = acdbHostApplicationServices()->workingDatabase();

    can not be loaded in 2008 "cannot find a procedure that it needs"
    but well in Map

    Any idea's?

    By Blogger Jelena, at 10:51 AM  

  • Hi Jelena,

    Really strange.
    All 2009 based products should be binary compatible with 2008 compiled ObjectARX applications.

    Did you test with a simple empty project created with ARXWizard?

    Regards,

    By Blogger Fernando Malard, at 10:57 AM  

  • Hi Fernando,

    Empty project made with ArxWizard works fine. Although, other arx projects not involving the line with

    "acdbHostApplicationServices()->workingDatabase();"

    are also working fine (like your MinimumApp).

    Temporary it’s no problem since I can work with Map.

    I'll post if I find something on this matter.

    By Blogger Jelena, at 11:16 AM  

  • Hi Fernando,
    I have objectIDs of some drawn entities..I want to create block of that entities..What should I do??
    Thanks in Advance..

    By Blogger shripad, at 5:31 AM  

  • shripad,

    The process consist of two steps:

    1) Create a BlockTableRecord to store the desired entities. At this step you will provide a unique name for the BlockTableRecord and will receive its ObjectId after created it.

    // Get the block table.
    AcDbBlockTable *pBlockTable = NULL;
    acdbHostApplicationServices()->workingDatabase()->getSymbolTable(pBlockTable, AcDb::kForWrite);

    // Create and name a new block table record.
    //
    AcGePoint3d oPt(0,0,0);

    AcDbBlockTableRecord *pBlockTableRec = new AcDbBlockTableRecord();
    pBlockTableRec->setName(_T("YourBlockName"));
    pBlockTableRec->setOrigin(oPt);

    // Add the new block table record to the block table.
    //
    AcDbObjectId blockTableRecordId;
    pBlockTable->add(blockTableRecordId, pBlockTableRec);
    pBlockTableRec->close();
    pBlockTable->close();

    2) With the BlockTableRecord ObjectId, use the deepCloneObjects() method as follows:

    // This is the array of ObjectId you will obtain
    AcDbObjectIdArray arrObjectID;

    AcDbIdMapping idMap;
    acdbHostApplicationServices()->workingDatabase()->deepCloneObjects(arrObjectID, blockTableRecordId, idMap);

    This is the outline process. I have not tested it but it should work.

    Regards,
    Fernando.

    By Blogger Fernando Malard, at 8:35 AM  

  • Hello Fernando !
    Im here to asking for helps or some suggestions plz !
    I got an error when calling an compiled help files (.CHM) in AutoCAD 2000,it says "The file you called isn't a Windows help file or has been corrupt。

    I got the same errors when i using .CHM files in AutoCAD 2005 or others

    I've found the codes and tips in setfunhelp functions but this is not work anyways
    ==============================
    (defun c:test ()
    (getstring "Press F1 for help on the foo command:")
    )
    (setfunhelp "c:test" "Help.chm" "Test01")

    ==============================

    On the other hand , the .hlp compiled files work properly when calling out in AutoCAD
    ==============================

    Could it be done in ObjectARX ? Mr Fernando !

    By Anonymous Anonymous, at 9:42 PM  

  • Hello,

    Can you open this CHM file with a double click?

    Windows consider CHM file as risky documents so when you try to open them from a network share or when you copy it from another computer it is locked by default.

    If it is the case, you need to right click the file into Windows Explorer, go to its properties and unlock the file.

    Regards,

    By Blogger Fernando Malard, at 8:26 AM  

  • hi fernando , i can open the CHM file with double click, but i can' found any properties setup or unlock likey options in any chm files, i guess there is format or support problem in windows xp


    how do i fix the chm format problem, how do i updates them ?
    Plz needs some advice .

    By Anonymous Anonymous, at 1:47 AM  

  • Sorry, I have no clue about that.

    One thing you can check is if the API you are using is compatible with the CHM version. One good test would be creating a standalone EXE application to open this CHM file. This will isolate the problem.

    Regards.

    By Blogger Fernando Malard, at 9:27 AM  

  • void inserirBlocos(AcGePoint3d point, TCHAR *nomeBlock)
    {
    AcDbBlockTable *bt;
    acdbHostApplicationServices()->workingDatabase()->getBlockTable(bt,AcDb::kForWrite);

    AcDbBlockTableRecord *btr;
    bt->getAt(ACDB_MODEL_SPACE, btr, AcDb::kForWrite);
    bt->close();

    AcDbBlockReference *br = new AcDbBlockReference(point, getIdBlockRec(nomeBlock));

    AcDbObjectId idIns;
    btr->appendAcDbEntity(idIns, br);

    btr->close();
    br->close();

    }

    and nothing appear on the screen!!

    By Anonymous Anonymous, at 11:13 AM  

  • Hello,

    First, you don't need to open the BlockTable for write to insert a block into the ModelSpace. Only the ModelSpace need to be opened for Write.

    Second, if your method getIdBlockRec() try to open again the BlockTable for Write, it will fail once you already opened it for Write.

    Third, the block must exist first into BlockTable before you can insert it into ModelSpace as a new BlockReference. I'm supposing your method create it.

    Regards,

    By Blogger Fernando Malard, at 11:18 AM  

  • This comment has been removed by the author.

    By Blogger keshan_n, at 8:49 AM  

  • Keshan,

    COPYCLIP is done via AcDbObject::wblockClone() where you will have access to the original object and the clone.

    With this information you may block, tweak or update any information during clone process.

    The second parameter is a pointer to the cloned object so I suppose if you delete it and pass back a NULL pointer it won't be cloned.

    Haven't tried that but it should work.

    Another possibility it to return something different from Acad::eOk as the ErrorStatus.

    Let me know if it works.
    Regards.

    By Blogger Fernando Malard, at 9:01 AM  

  • Hi,
    Can you please tell me how to read existing dwg file?

    Thank you in advance.

    Kirti.

    By Blogger kirti, at 11:11 AM  

  • Hi kirti,

    There is a sample under:

    \ObjectARX 2010\samples\database\testdb_dg

    It will show you how to do that.

    Regards.

    By Blogger Fernando Malard, at 3:15 PM  

  • hey all , i want to disable wheel mouse double clicking by writing an arx or any other way , but i dont know how ! please can somebody help me ?

    By Anonymous Anonymous, at 2:45 PM  

  • Hi, I have no idea how to do that.
    Did you take a look at the acad.pgp customization file?
    Have you tried the do it through CUI customization?

    Regards,

    By Blogger Fernando Malard, at 11:30 AM  

  • Hello,
    Why did you create an ObjectId to receive the new id if you won't use it?

    By Anonymous Anonymous, at 3:08 PM  

  • Hello,

    Actually AutoCAD database creates and returns the ObjectId when you append a new object to it.

    The code just stores the returned value into a local variable. The ObjectId can be further used, into the same DWG session, to retrieve the Object pointer through a Open/Close operation.

    It works like a database identification for the database client (our apps).

    Regards,

    By Blogger Fernando Malard, at 3:14 PM  

Post a Comment

<< Home