ObjectARX Development Notes - extended record

Keywords: Database

1 Description

In ObjectARX, in addition to Xdata, there is also an extended data -- Xrecord.
AcDbXrecord is a data storage class, similar to Xdata, but it has more data storage capacity and data storage types. Each AcDbXrecord object can store up to 2GB of data. The DXF group codes for the xrecord object range from 1 to 369. Xrecord is stored in a special entity, the extension dictionary, which can belong to an entity, so that the entity has custom extension data.
The extended dictionary is mentioned above. In order to understand the extended dictionary, we need to discuss the generation of the dictionary, which also involves the named object dictionary. The dictionary (AcDbDictionary class) is very similar to the symbol table, which is equivalent to the collection of object pointers, and retrieves the object through the unique string keyword index (char* text) and object ID (32-bit) number. The object design in the dictionary refers to the keyword string of an object ID number. A dictionary can contain any number of keyword strings. The ID number of the object may be any AcDbObject class or a class derived from the AcDbObject class. An object can only belong to one extension dictionary, but the extension dictionary can be accessed by any application. When an entity is deleted, its extended dictionary will also be deleted.

2 correlation function

2.1 AcDbObject class

The functions related to the extension dictionary in the entity AcDbObject class are as follows:

createExtensionDictionary()

  • Create an AcDbDictionary object and set it as the AcDbObject's extended dictionary. Acad:: eok, if successful. If the extension dictionary already exists, Acad:: ealreadyindb is returned.

extensionDictionary()

  • Returns the objectId of the extension dictionary owned by the entity. If not, the returned objectId is set to acdbobjectid:: knull.

releaseExtensionDictionary()

  • If the entity has an extension dictionary and does not contain any data, the extension dictionary is deleted. If successful, Acad:: eok is returned; if the extension dictionary is not empty, this function fails and Acad:: econtainernotempty is returned.

2.2 AcDbDictionary class

The functions commonly used in the AcDbDictionary class are as follows:

getAt()

  • Definition:
 Acad::ErrorStatus getAt(
    const ACHAR* entryName, 
    AcDbObject*& entryObj, 
    AcDb::OpenMode mode
) const;

Acad::ErrorStatus getAt(
    const ACHAR* entryName, 
    AcDbObjectId& entryObj
) const;
  • Parameters:
    entryName -- keyword index to be searched;
    entryObj -- return the pointer to the object;
    OpenMode mode -- the mode to open an object; there are three possible values, acdb:: kforread, acdb:: kforwrite, and acdb:: kfornotify.
  • Function: search for objects in the dictionary according to the key index. When found, the pointer entryObj points to the object and the search is case insensitive.
  • Return value: Acad:: eok if successful, Acad:: einvalidkey if entryName == NULL or entryName is not a valid keyword index, Acad:: ekeynotfound if no matching entry is found.

setAt()

  • Definition:
Acad::ErrorStatus setAt(
   const ACHAR* srchKey, 
   AcDbObject* newValue, 
   AcDbObjectId& retObjId
  • Parameters:
    srchKey -- the key index to the object to be added to the dictionary;
    newValue -- pointer to the object to be added to the dictionary;
    retObjId -- returns the object ID added to the dictionary.

  • Role: adds an object to the dictionary. If srchKey does not exist in the dictionary, this function adds the object specified by newValue to the dictionary. If the srchKey already exists, the original object is deleted and the new object pointed to by newValue is added to the dictionary.

  • Return value: Acad:: eok if successful, Acad:: einvalidkey if srchKey == NULL, Acad:: enullentitypoint if newValue == NULL.

remove ()

  • Definition:
Acad::ErrorStatus remove( AcDbObjectId objId );
Acad::ErrorStatus remove( const ACHAR * key );
Acad::ErrorStatus remove( const ACHAR * key,  AcDbObjectId& returnId );
  • Parameters:
    Key -- key index of the object to be deleted from the dictionary;
    objId -- the ID of the object to be deleted from the dictionary;
    returnId -- returns the ID of the deleted object in the dictionary.
  • Function: delete the object from the dictionary according to the key index or object ID. The deleted object is still in the database.
  • Return value: Acad:: eok if successful, Acad:: einvalidkey if key == NULL, Acad:: ekeynotfound if key or objId is not found in dictionary.

2.2 AcDbXrecord class

The common functions in the AcDbXrecord class of extended records are as follows:
rbChain()

  • Definition:
Acad::ErrorStatus rbChain(
   resbuf** ppRb, 
   AcDbDatabase* auxDb = NULL
) const;
  • Parameters:
    ppRb -- the address of the pointer of the data link to be added;

  • Function: get the restuff data link list of the extended record.

  • Return value: Acad:: eok if successful; Acad:: eoutofmemory if there is not enough memory to create a complete restuff data link list.

setFromRbChain()

  • Definition:
Acad::ErrorStatus setFromRbChain(
   const resbuf& pRb, 
   AcDbDatabase* auxDb = NULL
);
  • Parameters:
    pRb -- the restuff data link list containing extended records;

  • Function: add the resuf data link to the extended record. The contents of the data link are composed of the data type specified by DXF group code and the corresponding data. The following is a list of DXF group code ranges and the data types they represent, where DXF codes 330-369 are used to specify the object ID (ads_name).

DXF code (from) DXF code (stop) data type
1 4 text
6 9 text
10 17 point or vector (3 reals)
38 59 real
60 79 16-bit integer
90 99 32-bit integer
100 100 subclass data marker
102 102 text
140 149 real
170 179 16-bit integer
210 219 3 reals
270 279 16-bit integer
280 289 8-bit integer
300 309 text
310 319 binary chunk
320 329 handle
330 339 soft pointer ID
340 349 hard pointer ID
350 359 soft owner ID
360 369 hard owner ID
  • Return value: if the operation is successful, Acad:: eok will be returned; if the object IDDS [names] in the data link list is invalid, Acad:: einvalidadsname will be returned.

3 way of thinking

3.1 add extended record

By default, entities do not have an extended dictionary. Therefore, to save the extended record with the extended dictionary, you can follow the steps below:

  • Use the createExtensionDictionary() function to create an extension dictionary for the entity;
  • Use the extensionDictionary() function to get the extension Dictionary of the entity;
  • Use the setAt() function to add an object to the extension dictionary for storing extension records (other types of objects can also be stored);
  • Use the acutBuildList() function to create a data link list;
  • Use the setFromRbChain() function to add the created data link list to the extended record.

This completes the work of adding extended records for entities.

3.2 viewing extension records

To view the extended records of an entity:

  • Use the extensionDictionary() function to get the extension Dictionary of the entity;
  • Then get the extension record of the specified name through getAt() function;
  • The rbChain() function is used to obtain the data link list corresponding to the extended record;
  • Output the data in the data link list in a certain way.
    This completes viewing the data in the entity's extended records.

3.3 deleting extended records

To delete an extended record of an entity:

  • Use the extensionDictionary() function to get the extension Dictionary of the entity;
  • Use the remove() function to remove extended data of the specified name.
    This completes the deletion of the extended record of the entity.

4 steps

Create extended record Xrecord source code:

	//Add Xrecord to entity
	static void MyGroupcreateXrecord()
	{
		//Prompt user to select entity
		AcDbObject* pObj = NULL;
		if ((pObj = selectObject(AcDb::kForWrite)) == NULL) 
		{
			return;
		}

		//Prompt user for data to be added
		TCHAR xrecName[200], resString[200];
		xrecName[0] = resString[0] = _T('\0');

		acedGetString(NULL, _T("Enter Xrecord name: "),	xrecName);
		acedGetString(NULL, _T("Enter string to be added: "), resString);

		//Create an extended dictionary for entities
		pObj -> createExtensionDictionary();

		//Get extension Dictionary of entity
		AcDbObjectId dictObjId = pObj -> extensionDictionary();
		pObj -> close();

		//Add Xrecord to the extended dictionary
		AcDbDictionary* pDict = NULL;
		AcDbXrecord* pXrec = new AcDbXrecord;
		AcDbObjectId xrecObjId;
		if (acdbOpenObject(pDict, dictObjId, AcDb::kForWrite) == Acad::eOk)
		{
			pDict->setAt(xrecName, pXrec, xrecObjId);
			pDict->close();
		}

		//Create data link list
		struct resbuf* pRb = acutBuildList(AcDb::kDxfText, resString, RTNONE);

		//Add a data link to an extended record
		pXrec -> setFromRbChain(*pRb);

		pXrec -> close();
		acutRelRb(pRb);
	}

To view the extended record Xrecord source code:

	//Xrecord of output entity
	static void MyGrouplistXrecord()
	{
		//Prompt user to select entity
		AcDbObject* pObj = NULL;
		if ((pObj = selectObject(AcDb::kForRead)) == NULL) 
		{
			return;
		}
		
		//Get extension Dictionary of entity
		AcDbObjectId dictObjId = NULL;
		dictObjId = pObj->extensionDictionary();
		pObj -> close();
		if (dictObjId == AcDbObjectId::kNull)
		{
			acutPrintf(_T("\nNo Extension Dictionary found!"));
			return;
		}

		//Prompt user for Xrecord to find
		TCHAR xrecName[200];
		xrecName[0] = _T('\0');
		acedGetString(NULL, _T("Enter Xrecord to be listed: "), xrecName);

		//Get extended record
		AcDbDictionary *pDict = NULL;
		AcDbXrecord *pXrec = NULL;
		if (acdbOpenObject(pDict, dictObjId, AcDb::kForRead) == Acad::eOk)
		{
			pDict -> getAt(xrecName, (AcDbObject*&)pXrec, AcDb::kForRead);
			pDict -> close();
		}

		//Get data link list of extended records
		struct resbuf* pRb;
		pXrec -> rbChain(&pRb);
		pXrec -> close();

		//Output data in the command window
		printList(pRb);

		acutRelRb(pRb);
	}

Delete extended record Xrecord source code:

	//Delete Xrecord of entity
	static void MyGroupdeleteXrecord()
	{
		//Prompt user to select entity
		AcDbObject* pObj = NULL;
		if ((pObj = selectObject(AcDb::kForWrite)) == NULL) 
		{
			return;
		}

		//Get extension Dictionary of entity
		AcDbObjectId dictObjId = pObj -> extensionDictionary();	
		pObj -> close();
		if (dictObjId != AcDbObjectId::kNull)
		{
			//Prompt user for data to delete
			TCHAR xrecName[200];
			xrecName[0] = _T('\0');
			acedGetString(NULL, _T("Enter Xrecord name: "),	xrecName);

			//Remove Xrecord from extended dictionary
			AcDbDictionary* pDict = NULL;
			if (acdbOpenObject(pDict, dictObjId, AcDb::kForWrite) == Acad::eOk)
			{
				if (pDict -> has(xrecName))
				{
					pDict -> remove(xrecName);
					acutPrintf(_T("\n Deleted Xrecord %s!"), xrecName);
				}
				else
				{
					acutPrintf(_T("\nXrecord %s do not exists!"), xrecName);
				}

				pDict->close();
			}
		}
		else
		{
			acutPrintf(_T("\nNo Extension Dictionary exists!"));
			return;
		}
							
	}

Two functions need to be added, printList() to print Xdata information in the command window, and selectObject() to prompt the user to select an entity. The code is as follows:

	//Output Xrecord information in the command window
	static void printList(struct resbuf* pBuf)
	{
		int rt, i;
		TCHAR buf[133];

		for (i = 0;pBuf != NULL;i++, pBuf = pBuf->rbnext) {
			if (pBuf->restype < 0)
					rt = pBuf->restype;
			else if (pBuf->restype < 10)
				rt = RTSTR;
			else if (pBuf->restype < 38)
				rt = RT3DPOINT;
			else if (pBuf->restype < 60)
				rt = RTREAL;
			else if (pBuf->restype < 80)
				rt = RTSHORT;
			else if (pBuf->restype < 100)
				rt = RTLONG;
			else if (pBuf->restype < 106)
				rt = RTSTR;
			else if (pBuf->restype < 148)
				rt = RTREAL;
			else if (pBuf->restype < 290)
				rt = RTSHORT;
			else if (pBuf->restype < 330)
				rt = RTSTR;
			else if (pBuf->restype < 370)
				rt = RTENAME;
			else if (pBuf->restype < 999)
				rt = RT3DPOINT;
			else 
				rt = pBuf->restype; 

			switch (rt) 
			{
				case RTSHORT:
				if (pBuf->restype == RTSHORT)
					acutPrintf(_T("RTSHORT : %d\n"), pBuf->resval.rint);
				else
					acutPrintf(_T("(%d . %d)\n"), pBuf->restype, pBuf->resval.rint);
				break;
				case RTREAL:
				if (pBuf->restype == RTREAL)
					acutPrintf(_T("RTREAL : %0.3f\n"), pBuf->resval.rreal);
				else
					acutPrintf(_T("(%d . %0.3f)\n"), pBuf->restype, pBuf->resval.rreal);
				break;
			case RTSTR:
				if (pBuf->restype == RTSTR)
					acutPrintf(_T("RTSTR : %s\n"), pBuf->resval.rstring);
				else
					acutPrintf(_T("(%d . \"%s\")\n"), pBuf->restype, pBuf->resval.rstring);
				break;
			case RT3DPOINT:
				if (pBuf->restype == RT3DPOINT)
					acutPrintf(_T("RT3DPOINT : %0.3f, %0.3f, %0.3f\n"),
					pBuf->resval.rpoint[X],
					pBuf->resval.rpoint[Y],
					pBuf->resval.rpoint[Z]);
				else
					acutPrintf(_T("(%d %0.3f %0.3f %0.3f)\n"),
					pBuf->restype,
					pBuf->resval.rpoint[X],
					pBuf->resval.rpoint[Y],
					pBuf->resval.rpoint[Z]);
				break;
			case RTLONG:
				acutPrintf(_T("RTLONG : %dl\n"), pBuf->resval.rlong);
				break;
			case -1:
			case RTENAME: 
				acutPrintf(_T("(%d . <Entity name: %8lx>)\n"),
					pBuf->restype, pBuf->resval.rlname[0]);
				break;
			case -3: 
				acutPrintf(_T("(-3)\n"));
			}

			if ((i == 23) && (pBuf->rbnext != NULL)) 
			{
				i = 0;
				acedGetString(0, _T("Press <ENTER> to continue..."), buf);
			}
		}
		return;
	}

	//Prompt user to select entity
	static AcDbObject*	selectObject(AcDb::OpenMode openMode)
	{
		int ss;
		ads_name en;
		ads_point pt;
		acedInitGet(RSG_OTHER, _T("Handle _Handle"));
		ss = acedEntSel(_T("\nSelect an Entity or enter")_T(" 'H' to enter its handle:  "), en, pt);

		TCHAR handleStr[132];
		AcDbObjectId eId;
		switch (ss) 
		{
		case RTNORM:   
			break;
		case RTKWORD:
			if ((acedGetString(Adesk::kFalse,
				_T("Enter Valid Object Handle: "),
				handleStr) == RTNORM)
				&& (acdbHandEnt(handleStr, en) == RTNORM))
			{
				break;
			}

		default:
			acutPrintf(_T("Nothing Selected, Return Code==%d\n"),ss);
			return NULL;
		}

		Acad::ErrorStatus retStat;
		retStat = acdbGetObjectId(eId, en);
		if (retStat != Acad::eOk) {
			acutPrintf(_T("\nacdbGetObjectId failed"));
			acutPrintf(_T("\nen==(%lx,%lx), retStat==%d\n"),
				en[0], en[1], eId);
			return NULL;
		}

		AcDbObject* obj;

		if ((retStat = acdbOpenObject(obj, eId, openMode))
			!= Acad::eOk)
		{
			acutPrintf(_T("acdbOpenEntity failed: ename:(%lx,%lx),")
				_T(" mode:%d retStat:%d"), en[0], en[1],
				openMode, retStat);
			return NULL;
		}
		return obj;
	}

5 Effect

As shown in the figure below, the entity originally does not contain any extended data.

Through the program, you can add an extended record named "xrecord" to the entity, storing a string "this is a test string", as shown in the following figure.


After using the delete command, there are no Xrecord objects in the entity's extended dictionary, as shown in the following figure.

Compared with Xdata, xrecord stores more data and more data types. Xrecord uses standard AutoCAD standard group codes, which enables xrecord to store all types of objects, even another xrecord.
At the same time, other. arx and AutoLISP programs in AutoCAD can directly access the Xrecord data. If data security is not an issue, then xrecords is a good data storage solution. If data security is a problem, then custom data storage classes are a better choice.

6 source code

Source code: xrecord
Extraction code: 2bis

Reference document
[1]:Autodesk ObjectARX for AutoCAD 2015: Developer Guide.

Published 3 original articles, praised 0 and visited 104
Private letter follow

Posted by Dave Liebman on Wed, 19 Feb 2020 04:08:34 -0800