ObjectARX开发笔记(二)——使用Xdata向AutoCAD图形追加自定义数据

1.自定义数据

ObjectARX开发过程中,有时会使用自定义数据,主要包括:

  • 自定义对象——可以让用户按照自己希望的方式封装数据,并向AutoCAD中添加第三方实体类型,这些实体类型用于与AutoCAD自身实体一样的特性,ObjectARX程序能够灵活控制自定义实体的显示。
  • 扩充数据——可以向图形本身或者图形中的实体追加一些数据。
    扩充数据又分为Xdata(eXtension data)和 Xrecord(eXtension record)两种。

2.Xdata

  • AutoCAD数据库的任何对象都可以灵活的附加一定数量的扩展数(Xdata),供开发者使用。
  • 扩展数据的含义由开发者自行解释,AutoCAD只维护这些数据而不管其具体的含义。
  • 扩展数据是链表组,每个组是链表系列,以一个名字开头以做标识。这个名字是应用程序名。
  • 应用程序名通过acdbRegApp()进行注册,最长可达31个字符 所附着的数据链表不能超过16K。
  • DXF组码只能采用1000~1071之间的组码值。

2.1 Xdata相关函数—acdbRegApp

定义

int acdbRegApp(
    const ACHAR * appname
);

作用:在当前图形中注册一个应用程序名称,用于分组,存储,检索和修改实体的扩展数据。appname必须遵守符号表名称(例如图层名称)的规则,且不能和已经存在的RegApp重复。每个新名称将产生一个新的acdbRegApp记录。
返回值

  • appname注册成功,返回RTNORM;
  • appname已存在,返回RTERROR;
  • appname注册失败,返回一个表明失败原因的系统变量ERRNO。

2.2 Xdata相关函数—setXData

定义

virtual Acad::ErrorStatus setXData(
    const resbuf* xdata
);

作用:设置一个对象的扩展数据。

  • 每个注册(regappname)链表以 restype = 1001 分界,并且resval.rstring应该是合法字符。
  • RegappName必须是AcDbRegAppTable中存在的。
  • 如果xdata中已经有该regappname组, 则新的链表将覆盖之 。
  • 要清除xdata中某个regappname的所有内容,只需建立一个以此regappname开头的空链表即可。

返回值

  • 如果xdata成功添加到对象,则返回Acad :: eOk。
  • 如果对象的xdata区域中没有足够的空间,则返回Acad::eXdataSizeExceeded。
  • 如果xdata中的所有regappName均不存在,则返回Acad :: eRegappIdNotFound。

2.3 Xdata相关函数—XData

定义

virtual resbuf* xData(
    const ACHAR* regappName = nullptr
) const;

作用:获取一个对象中名为“regappName”的扩展数据。如果regappName == NULL,则返回所有xdata,否则仅返回指定名称为regappName的xdata。如果需要多个regappName的xdata,则必须对该成员函数进行多次调用,每个regappName调用一次。
返回值

  • 返回包含对象xdata副本的resbuf结构的链接列表。

2.4 Xdata实例,添加、查看和删除Xdata

添加Xdata代码如下:

//添加Xdata
	static void MyGroupaddXdata() 
	{
		//提示用户选择对象
		AcDbObject* pObj = selectObject(AcDb::kForRead);
		if (!pObj) {
			acutPrintf(_T("Error selecting object\n"));
			return;
		}

		//获取Xdata名称
		TCHAR appName[132], resString[200];
		appName[0] = resString[0] = _T('\0');

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

		struct  resbuf  *pRb, *pTemp;

		pRb = pObj->xData(appName);

		if (pRb != NULL) 
		{
			//如果Xdata已经存在,不执行任何操作
			for (pTemp = pRb; pTemp->rbnext != NULL;pTemp = pTemp->rbnext)
			{ 
				; 
			}
		} 
		else 
		{
			//如果Xdata不存在,向当前对象注册名为appName的Xdata
			acdbRegApp(appName);

			pRb = acutNewRb(AcDb::kDxfRegAppName);
			pTemp = pRb;
			pTemp->resval.rstring
				= (TCHAR*) malloc((_tcslen(appName) + 1) * sizeof(TCHAR));
			_tcscpy(pTemp->resval.rstring, appName);
		}

		//向新注册的Xdata添加自定义扩展数据
		pTemp->rbnext = acutNewRb(AcDb::kDxfXdAsciiString);
		pTemp = pTemp->rbnext;
		pTemp->resval.rstring
			= (TCHAR*) malloc((_tcslen(resString) + 1) * sizeof(TCHAR));
		_tcscpy(pTemp->resval.rstring, resString);

		//把对象从AcDb::kForRead 状态变成AcDb::kForWrite状态
		pObj->upgradeOpen();

		pObj->setXData(pRb);

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

查看Xdata代码如下:

//显示Xdata
static void MyGroupprintXdata()
	{
		//提示用户选择对象
		AcDbObject *pObj;
		if ((pObj = selectObject(AcDb::kForRead)) == NULL) {
			return;
		}

		//获取Xdata名称
		TCHAR appname[133];
		if (acedGetString(NULL,
			_T("\nEnter the desired Xdata application name: "),
			appname) != RTNORM)
		{
			return;
		}

		// 根据Xdata名称appname,调用printList()函数输出Xdata信息
		struct resbuf *pRb;
		pRb = pObj->xData(appname);

		if (pRb != NULL) 
		{
			printList(pRb);
			acutRelRb(pRb);

		} else {
			acutPrintf(_T("\nNo xdata for this appname"));
		}
		pObj->close();
	}

删除Xdata代码如下:

//删除Xdata
	static void MyGroupdeleteXdata()
	{
		AcDbObject* pObj = selectObject(AcDb::kForRead);
		if (!pObj) {
			acutPrintf(_T("Error selecting object\n"));
			return;
		}

		//获取Xdata名称		
		TCHAR appName[132];
		appName[0] = _T('\0');
		acedGetString(NULL, _T("Enter application name: "), appName);

		//在pObj的Xdata中查找appName
		struct resbuf *pTemp = nullptr; 
		pTemp = pObj -> xData(appName);

		if (pTemp != nullptr)
		{
			// 删除名为appName的Xdata
			acdbRegApp(appName);

			struct resbuf* pRb = acutBuildList(AcDb::kDxfRegAppName, appName, RTNONE);
			pObj->upgradeOpen();
			pObj->setXData(pRb);
			acutRelRb(pRb);

			acutPrintf(TEXT("\nXdata deleted successfully."));
		}
		else
		{
			//未找到需要删除的Xdata
			acutPrintf(TEXT("\nNo Xdata found."));
		}

		acutRelRb(pTemp);
		pObj->close();
	}

还需要添加两个函数,printList()用于在命令窗口中打印Xdata信息;selectObject()用于提示用户选择实体。代码如下:

//在AutoCAD命令窗口中打印Xdata信息
	static void printList(struct resbuf* pRb)
	{
		int rt, i;
		TCHAR buf[133];

		for (i = 0;pRb != NULL;i++, pRb = pRb->rbnext) 
		{
			if (pRb->restype < 1010) 
			{
				rt = RTSTR;
			} 
			else if (pRb->restype < 1040) 
			{
				rt = RT3DPOINT;
			} 
			else if (pRb->restype < 1060) 
			{
				rt = RTREAL;
			} 
			else if (pRb->restype < 1071) 
			{
				rt = RTSHORT;
			} 
			else if (pRb->restype == 1071) 
			{
				rt = RTLONG;
			} 
			else 
			{
				rt = pRb->restype; 
			}

			switch (rt) 
			{
				case RTSHORT:
				if (pRb->restype == RTSHORT) 
				{
					acutPrintf(
						_T("RTSHORT : %d\n"), pRb->resval.rint);
				} 
				else 
				{
					acutPrintf(_T("(%d . %d)\n"), pRb->restype,
						pRb->resval.rint);
				};
				break;

				case RTREAL:
				if (pRb->restype == RTREAL) 
				{
					acutPrintf(_T("RTREAL : %0.3f\n"),
						pRb->resval.rreal);
				} 
				else 
				{
					acutPrintf(_T("(%d . %0.3f)\n"), pRb->restype,
						pRb->resval.rreal);
				};
				break;

				case RTSTR:
				if (pRb->restype == RTSTR) 
				{
					acutPrintf(_T("RTSTR : %s\n"),
						pRb->resval.rstring);
				} 
				else 
				{
					acutPrintf(_T("(%d . \"%s\")\n"), pRb->restype,
						pRb->resval.rstring);
				};
				break;

				case RT3DPOINT:
				if (pRb->restype == RT3DPOINT) 
				{
					acutPrintf(
						_T("RT3DPOINT : %0.3f, %0.3f, %0.3f\n"),
						pRb->resval.rpoint[X],
						pRb->resval.rpoint[Y],
						pRb->resval.rpoint[Z]);
				} 
				else {
					acutPrintf(_T("(%d %0.3f %0.3f %0.3f)\n"),
						pRb->restype,
						pRb->resval.rpoint[X],
						pRb->resval.rpoint[Y],
						pRb->resval.rpoint[Z]);
				}
				break;

				case RTLONG:
				acutPrintf(_T("RTLONG : %dl\n"), pRb->resval.rlong);
				break;
			}

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

	//提示用户选择实体
	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;
	}

3.代码效果

使用“addXdata”命令选择实体并添加扩展数据,“printXdata”命令在AutoCAD的命令窗口显示添加的扩展数据,“deleteXdata”命令删除扩展数据。如下图所示,使用Xdata向图中被选中直线追加了自定义数据。
ObjectARX开发笔记(二)——使用Xdata向AutoCAD图形追加自定义数据_第1张图片

4.源代码

源代码:xData
提取码:4bbm

参考文档
[1]:Autodesk ObjectARX for AutoCAD 2015: Developer Guide.

你可能感兴趣的:(ObjectARX开发笔记)