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.