天天看點

ObjectARX開發筆記(三)——擴充記錄(Xrecord)1 說明2 相關函數3 思路4 步驟5 效果6 源代碼

1 說明

在ObjectARX中,除了Xdata以外,還有一種擴充資料——Xrecord。

AcDbXrecord是一種資料存儲類,與Xdata類似,但其資料存儲量和資料存儲類型更多,每個AcDbXrecord對象最多可存儲2GB的資料。Xrecord對象的DXF組碼範圍為1~369。Xrecord存儲在一種特殊實體——擴充字典中,而擴充字典可以屬于一個實體,這樣這個實體就擁有了自定義的擴充資料。

上面提到了擴充字典,為了了解擴充字典,需要讨論字典的生成,這裡也涉及到有名對象字典。字典(AcDbDictionary類)與符号表非常類似,相當于是對象的指針的集合,并通唯一的字元串關鍵字索引(char* text )和對象ID(32-bit )号檢索對象。在字典中的對象設計上是指一個對象ID号的關鍵字字元串。字典中能夠包含任意數量的關鍵字字元串。對象的ID号可能是任何的AcDbObject類或由AcDbObject類派生的類。一個對象隻能屬于一個擴充字典,但是該擴充字典能夠由任何應用程式通路。當一個實體被删除,其擴充字典也将被删除。

2 相關函數

2.1 AcDbObject類

實體AcDbObject類中與擴充字典相關的函數主要有:

createExtensionDictionary()

  • 建立一個AcDbDictionary對象,并将其設定為AcDbObject的擴充字典。如果成功,則傳回Acad :: eOk。如果擴充字典已經存在,則傳回Acad :: eAlreadyInDb。

extensionDictionary()

  • 傳回實體擁有的擴充字典的objectId, 如果沒有,則将傳回的objectId設定為AcDbObjectId :: kNull。

releaseExtensionDictionary()

  • 如果實體有擴充字典,且不含任何資料,則删除該擴充字典。如果成功,則傳回Acad :: eOk;如果擴充字典不為空,則此函數失敗并傳回Acad :: eContainerNotEmpty。

2.2 AcDbDictionary類

字典AcDbDictionary類中常用函數主要有:

getAt()

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

Acad::ErrorStatus getAt(
    const ACHAR* entryName, 
    AcDbObjectId& entryObj
) const;
           
  • 參數:

    entryName —— 要查找的關鍵字索引;

    entryObj —— 傳回指向對象的指針;

    OpenMode mode——打開對象的模式;有三個可能值,AcDb :: kForRead,AcDb :: kForWrite和AcDb :: kForNotify。

  • 作用:在字典中根據關鍵字索引搜尋對象。找到後,指針entryObj指向對象,搜尋不區分大小寫。
  • 傳回值:如果成功,則傳回Acad :: eOk;如果entryName == NULL或entryName不是有效的關鍵字索引,則傳回Acad :: eInvalidKey; 如果找不到比對的條目,則傳回Acad :: eKeyNotFound。

setAt()

  • 定義:
Acad::ErrorStatus setAt(
   const ACHAR* srchKey, 
   AcDbObject* newValue, 
   AcDbObjectId& retObjId
           
  • 參數:

    srchKey —— 要添加到字典中的指向對象的關鍵字索引;

    newValue —— 指向要添加到字典中的對象的指針;

    retObjId —— 傳回被添加到字典中的對象ID。

  • 作用:向字典中添加對象。如果srchKey在字典中不存在,此函數将newValue指定的對象添加到字典中。 如果srchKey已存在,則将原來的對象删除,newValue指向的新對象添加到字典中。
  • 傳回值:如果成功,則傳回Acad :: eOk; 如果srchKey == NULL,傳回Acad :: eInvalidKey; 如果newValue == NULL,則為Acad :: eNullEntityPointer。

remove ()

  • 定義:
Acad::ErrorStatus remove( AcDbObjectId objId );
Acad::ErrorStatus remove( const ACHAR * key );
Acad::ErrorStatus remove( const ACHAR * key,  AcDbObjectId& returnId );
           
  • 參數:

    key —— 要從字典中删除的對象的關鍵字索引;

    objId —— 要從字典中删除的對象的ID;

    returnId —— 傳回字典中被删除的對象的ID。

  • 作用:根據關鍵字索引或對象ID從字典中删除對象。被删除的對象仍在資料庫中。
  • 傳回值:如果成功,則傳回Acad :: eOk;如果key == NULL,則傳回Acad :: eInvalidKey;如果在字典中找不到key或objId,則傳回Acad :: eKeyNotFound。

2.2 AcDbXrecord類

擴充記錄AcDbXrecord類中常用函數主要有:

rbChain()

  • 定義:
Acad::ErrorStatus rbChain(
   resbuf** ppRb, 
   AcDbDatabase* auxDb = NULL
) const;
           
  • 參數:

    ppRb —— 要添加的資料連結清單的指針的位址;

  • 作用:擷取擴充記錄的resbuf資料連結清單。
  • 傳回值:如果成功,則傳回Acad :: eOk;如果沒有足夠的記憶體來建立完整的resbuf資料連結清單,則傳回Acad :: eOutOfMemory。

setFromRbChain()

  • 定義:
Acad::ErrorStatus setFromRbChain(
   const resbuf& pRb, 
   AcDbDatabase* auxDb = NULL
);
           
  • 參數:

    pRb —— 包含擴充記錄的resbuf資料連結清單;

  • 作用:将resbuf資料連結清單添加到擴充記錄中,資料連結清單中的内容由DXF組碼指定的資料類型,及對應的資料組成。以下是DXF組代碼範圍及其代表的資料類型的清單,其中DXF碼330~369用于指定對象ID(ads_name)。
DXF碼(起) DXF碼(止) 資料類型
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
  • 傳回值:如果操作成功,則傳回Acad :: eOk;如果資料連結清單中的對象ID無效,則傳回Acad :: eInvalidAdsName。

3 思路

3.1 添加擴充記錄

在預設的情況下,實體是沒有擴充字典的。是以,要利用擴充字典儲存擴充記錄,可以按以下步驟:

  • 使用createExtensionDictionary()函數為實體建立擴充字典;
  • 使用extensionDictionary()函數擷取實體的擴充字典;
  • 使用setAt()函數為擴充字典添加一個對象,用于存儲擴充記錄(也可以存儲其他類型的對象);
  • 使用acutBuildList()函數建立一個資料連結清單;
  • 使用setFromRbChain()函數将建立的資料連結清單添加到擴充記錄中。

這樣就完成了為實體添加擴充記錄的工作。

3.2 檢視擴充記錄

檢視實體的擴充記錄的步驟為:

  • 使用extensionDictionary()函數擷取實體的擴充字典;
  • 然後通過getAt()函數擷取指定名稱的擴充記錄;
  • 使用rbChain()函數擷取擴充記錄對應的資料連結清單;
  • 按照一定的方式輸出資料連結清單中的資料。

這樣就完成了檢視實體的擴充記錄中的資料。

3.3 删除擴充記錄

删除實體的擴充記錄的步驟為:

  • 使用extensionDictionary()函數擷取實體的擴充字典;
  • 使用remove()函數删除指定名稱的擴充資料。

這樣就完成了删除實體的擴充記錄。

4 步驟

建立擴充記錄Xrecord源代碼:

//為實體添加Xrecord
	static void MyGroupcreateXrecord()
	{
		//提示使用者選擇實體
		AcDbObject* pObj = NULL;
		if ((pObj = selectObject(AcDb::kForWrite)) == NULL) 
		{
			return;
		}

		//提示使用者輸入要添加的資料
		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);

		//為實體建立擴充字典
		pObj -> createExtensionDictionary();

		//擷取實體的擴充字典
		AcDbObjectId dictObjId = pObj -> extensionDictionary();
		pObj -> close();

		//向擴充字典中添加Xrecord
		AcDbDictionary* pDict = NULL;
		AcDbXrecord* pXrec = new AcDbXrecord;
		AcDbObjectId xrecObjId;
		if (acdbOpenObject(pDict, dictObjId, AcDb::kForWrite) == Acad::eOk)
		{
			pDict->setAt(xrecName, pXrec, xrecObjId);
			pDict->close();
		}

		//建立資料連結清單
		struct resbuf* pRb = acutBuildList(AcDb::kDxfText, resString, RTNONE);

		//将資料連結清單添加到擴充記錄中
		pXrec -> setFromRbChain(*pRb);

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

檢視擴充記錄Xrecord源代碼:

//輸出實體的Xrecord
	static void MyGrouplistXrecord()
	{
		//提示使用者選擇實體
		AcDbObject* pObj = NULL;
		if ((pObj = selectObject(AcDb::kForRead)) == NULL) 
		{
			return;
		}
		
		//擷取實體的擴充字典
		AcDbObjectId dictObjId = NULL;
		dictObjId = pObj->extensionDictionary();
		pObj -> close();
		if (dictObjId == AcDbObjectId::kNull)
		{
			acutPrintf(_T("\nNo Extension Dictionary found!"));
			return;
		}

		//提示使用者輸入要查找的Xrecord
		TCHAR xrecName[200];
		xrecName[0] = _T('\0');
		acedGetString(NULL, _T("Enter Xrecord to be listed: "), xrecName);

		//擷取擴充記錄
		AcDbDictionary *pDict = NULL;
		AcDbXrecord *pXrec = NULL;
		if (acdbOpenObject(pDict, dictObjId, AcDb::kForRead) == Acad::eOk)
		{
			pDict -> getAt(xrecName, (AcDbObject*&)pXrec, AcDb::kForRead);
			pDict -> close();
		}

		//擷取擴充記錄的資料連結清單
		struct resbuf* pRb;
		pXrec -> rbChain(&pRb);
		pXrec -> close();

		//在指令視窗中輸出資料
		printList(pRb);

		acutRelRb(pRb);
	}
           

删除擴充記錄Xrecord源代碼:

//删除實體的Xrecord
	static void MyGroupdeleteXrecord()
	{
		//提示使用者選擇實體
		AcDbObject* pObj = NULL;
		if ((pObj = selectObject(AcDb::kForWrite)) == NULL) 
		{
			return;
		}

		//擷取實體的擴充字典
		AcDbObjectId dictObjId = pObj -> extensionDictionary();	
		pObj -> close();
		if (dictObjId != AcDbObjectId::kNull)
		{
			//提示使用者輸入要删除的資料
			TCHAR xrecName[200];
			xrecName[0] = _T('\0');
			acedGetString(NULL, _T("Enter Xrecord name: "),	xrecName);

			//删除擴充字典中的Xrecord
			AcDbDictionary* pDict = NULL;
			if (acdbOpenObject(pDict, dictObjId, AcDb::kForWrite) == Acad::eOk)
			{
				if (pDict -> has(xrecName))
				{
					pDict -> remove(xrecName);
					acutPrintf(_T("\n已删除Xrecord %s!"), xrecName);
				}
				else
				{
					acutPrintf(_T("\nXrecord %s do not exists!"), xrecName);
				}

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

還需要添加兩個函數,printList()用于在指令視窗中列印Xdata資訊;selectObject()用于提示使用者選擇實體。代碼如下:

//在指令視窗中輸出Xrecord資訊
	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;
	}

	//提示使用者選擇實體
	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 效果

如下圖所示,實體中原本不含任何擴充資料。

ObjectARX開發筆記(三)——擴充記錄(Xrecord)1 說明2 相關函數3 思路4 步驟5 效果6 源代碼

通過程式可為實體添加一個名為“xrecord”的擴充記錄,存儲了一個字元串“這是一個測試字元串”,如下圖所示。

ObjectARX開發筆記(三)——擴充記錄(Xrecord)1 說明2 相關函數3 思路4 步驟5 效果6 源代碼
ObjectARX開發筆記(三)——擴充記錄(Xrecord)1 說明2 相關函數3 思路4 步驟5 效果6 源代碼

使用删除指令之後,實體的擴充字典中不在有任何Xrecord對象,如下圖所示。

ObjectARX開發筆記(三)——擴充記錄(Xrecord)1 說明2 相關函數3 思路4 步驟5 效果6 源代碼

相比于Xdata,Xrecord存儲的資料量更大,資料類型更多。Xrecord使用了标準的AutoCAD标準組碼,這使得Xrecord可以存儲所有類型的對象,甚至是另一個Xrecord。

同時,AutoCAD中其他的.arx和AutoLISP程式都可以直接通路Xrecord資料。如果資料安全不是問題,那麼xrecords是一個很好的資料存儲方案。 如果資料安全是一個問題,那麼自定義資料存儲類是更好的選擇。

6 源代碼

源代碼:xrecord

提取碼:2bis

參考文檔

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

繼續閱讀