下载地址:
http://www.mxcad.net:2080/cpp/Tech-XData.zip
在...MxDraw52\Src\MxDraw5.2\samples\Edit的实例中,我们有以下的扩展数据演示功能:

此实例将进一步演示如何获取实体上的扩展数据并修改,演示程序运行如下图:

打开CAD图纸:
在“打开图纸”按钮的单击事件中,我们添加如下函数:
void CTechXDataDlg::OnBnClickedOpenMap()
{
acDocManager->sendStringToExecute(Mx::mcdbCurDwg()->GetDocument(), L"OpenDwg");
}事件中的代码大意为调用我们之前准备的命令,接口如下:
// Summary: // 向控件发送一个命令,并执行该命令 // Parameters: // pAcTargetDocument - 执行命令的对象 // pszExecute - 执行命令的对象 // bActivate - 暂没有使用 // bWrapUpInactiveDoc - 暂没有使用 // bEchoString - 暂没有使用 // pParam - 执行命令时,可以带参数执行,用户不需要负责它的内存释放,将由sendStringToExecute函数自动释放 // Returns: // 如果成功返回Mcad::eOk,如果传递的数据非法则返回Mcad::eInvalidInput virtual Mcad::ErrorStatus sendStringToExecute(McApDocument* pAcTargetDocument, LPCTSTR pszExecute, bool bActivate = true, bool bWrapUpInactiveDoc = false, bool bEchoString = true, struct resbuf* pParam = NULL, bool bFunCall = false ) = 0;
而调用的命令,我们需要做出如下的准备,如“选择实体”按钮单击事件中的代码段:
void CTechXDataDlg::OnBnClickedChooseButton()
{
Mx::mcDocManager()->sendStringToExecute(MxDraw::GetDatabase(m_hDrawOcx)->GetDocument()
, _T("CET"));
InitToPanle();
}我们调用的“CET”命令,该命令为我们的自定义命令,我们做出了如下的声明及实现:

命令代码声明如下:
static void ChooseEnt();
命令代码实现如下:
void CTechXDataDlg::ChooseEnt()
{
MrxDbgUiPrEntity mChooseEnt(L"选择一个实体");
if (MrxDbgUiPrBase::kOk == mChooseEnt.go())
{
m_mId = mChooseEnt.objectId();
}
else
{
m_mId.setNull();
}
}在完成命令的具体功能之后,我们将其注册到系统,以供调用,注册命命令声明如下:
static void RegisterCommand();
实现如下:
void CTechXDataDlg::RegisterCommand()
{
acedRegCmds->addCommand(_T("SysCmd"), _T("CET"), _T("CET"),
MCRX_CMD_MODAL, ChooseEnt);
}我们在面板的初始化函数中调用它:

至此我们即完成了自定义命令的所有步骤,需要说明的是,与用户交互的操作,比如我们的自定义命令“ChooseEnt”即是提示用户选择一个实体,必须放在自定义命令中执行。
在 ...\MxDraw52\Src\MxDraw5.2\ArxInc\Mdsdef.h中,我们看到了链表的如下定义:
#ifndef _mdsdef_h
#define _mdsdef_h 1
#define TRUE 1
#define FALSE 0
#define EOS '\0'
#pragma pack(push, 8)
typedef double mds_real;
typedef mds_real mds_point[3];
typedef long mds_name[2];
typedef mds_real mds_matrix[4][4];
typedef mds_real *mds_pointp;
typedef long *mds_namep;
#ifdef X
#undef X
#endif
#ifdef Y
#undef Y
#endif
#ifdef Z
#undef Z
#endif
#ifdef T
#undef T
#endif
#ifndef _XYZT_DEFINED
#define _XYZT_DEFINED
enum { X = 0, Y = 1, Z = 2 };
enum { T = 3 };
#endif
#define PAUSE "\\"
enum {
RSG_NONULL = 0x01,
RSG_NOZERO = 0x02,
RSG_NONEG = 0x04,
RSG_NOLIM = 0x08,
RSG_GETZ = 0x10,
RSG_DASH = 0x20,
RSG_2D = 0x40,
RSG_OTHER = 0x80,
RSG_DDISTFIRST = 0x100,
RSG_NOXORDRAG = 0x200,
RSG_DYNXYCOORDINPUT = 0x400,
REG_DYNANGELINPUT = 0x800,
REG_DISTANCEINPUT = 0x1000,
RET_AUTOINPUT = 0x2000
};
enum {
INP_NNULL = RSG_NONULL,
INP_NZERO = RSG_NOZERO,
INP_NNEG = RSG_NONEG,
INP_NLIM = RSG_NOLIM,
INP_DASH = RSG_DASH,
INP_NZCOORD = RSG_2D
};
struct mds_binary {
short clen;
char *buf;
};
union mds_u_val {
mds_real rreal;
mds_real rpoint[3];
short rint;
LPTSTR rstring;
long rlname[2];
long rlong;
struct mds_binary rbinary;
unsigned char ihandle[8];
/*#ifdef _WIN64*/
__int64 objId;
// #else
// long objId;
// #endif
void* pObj;
};
struct resbuf {
struct resbuf *rbnext;
short restype;
union mds_u_val resval;
resbuf()
{
restype = 5003 ; // RTSHORT
rbnext = 0;
resval.rint = 0;
}
};
typedef struct resbuf *pResbuf;
typedef const struct resbuf *kpResbuf;
#pragma pack(pop)
#endif可以看到的是,链表支持如,整形、句柄、指针、字符串的存储,而在存储中,对于每一个节点的类型设置“restype”必须与存储的数据类型(mds_u_val联合下的数据类型)对应,如此实例中的设置扩展数据代码段:
resbuf* pExDataRb = nullptr;
std::vector <resbuf*> vBuf;
resbuf* pLastNode = nullptr;
auto CreateNewXData = [&]() {
pLastNode = pExDataRb = acutBuildList(AcDb::kDxfRegAppName, sTemp, 0);
m_mRealNum.GetWindowTextW(sTemp);
if (sTemp.GetLength())
{
auto pBuf = acutBuildList(AcDb::kDxfXdReal, _wtof(sTemp), 0);
pLastNode->rbnext = pBuf;
pLastNode = pLastNode->rbnext;
vBuf.push_back(pBuf);
}
m_mShortNum.GetWindowTextW(sTemp);
if (sTemp.GetLength())
{
auto pBuf = acutBuildList(AcDb::kDxfXdInteger16, _wtoi(sTemp), 0);
pLastNode->rbnext = pBuf;
pLastNode = pLastNode->rbnext;
vBuf.push_back(pBuf);
}
m_mLongNum.GetWindowTextW(sTemp);
if (sTemp.GetLength())
{
auto pBuf = acutBuildList(AcDb::kDxfXdInteger32, _wtoi(sTemp), 0);
pLastNode->rbnext = pBuf;
pLastNode = pLastNode->rbnext;
vBuf.push_back(pBuf);
}
m_mString.GetWindowTextW(sTemp);
if (sTemp.GetLength())
{
auto pBuf = acutBuildList(AcDb::kDxfXdAsciiString, sTemp, 0);
pLastNode->rbnext = pBuf;
pLastNode = pLastNode->rbnext;
vBuf.push_back(pBuf);
}
CString sTempY, sTempZ;
{
m_m3dVectorX.GetWindowTextW(sTemp);
if (!sTemp.GetLength())
return;
m_m3dVectorY.GetWindowTextW(sTempY);
if (!sTempY.GetLength())
return;
m_m3dVectorZ.GetWindowTextW(sTempZ);
if (!sTempZ.GetLength())
return;
mds_real vVec[] = { _wtof(sTemp),_wtof(sTempY),_wtof(sTempZ) };
auto pBuf = acutBuildList(AcDb::kDxfXdXCoord, vVec, 0);
pLastNode->rbnext = pBuf;
pLastNode = pLastNode->rbnext;
vBuf.push_back(pBuf);
}
};
上述代码段中,我们再次使用到了acutBuildList宏,在构造选择集的介绍文章中我们已对该宏进行了介绍,再次不再赘述。而在构造扩展数据时,大致的结构如下:
可以看到的是,数据都是以程序名(即 kDxfRegAppName = 1001)开始,而在后挂在相关的信息。
获取、修改、删除扩展数据
获取扩展数据的接口如下:
virtual struct resbuf* xData (
LPCTSTR pszRegappName = NULL - 扩展数据应用名,如果为空返回所有扩展数据
) const;
设置扩展数据的接口如下:
virtual Mcad::ErrorStatus setXData(
const struct resbuf* xdata - 扩展数据链表指针,在不使用时调用Mx::mcutRelRb释放链表
);
需要注意的是:如果我们创建了已有的应用程序名并设置到扩展数据,将会覆盖之前该应用名下的数据,在本实例中,我们通过提示用户的方式,选择覆盖、加入尾部(合并)的方式来添加,代码如下:
McDbObjectPointer<AcDbEntity> spEnt(m_mId, AcDb::kForWrite);
if (spEnt.openStatus() == Acad::eOk)
{
auto pData = spEnt->xData(sTemp);
if (pData)
{
auto iResult = MxDraw::MxMessageBox(L"已有该程序名的扩展数据,YES[覆盖] NO[合并] CANCLE[取消]", MB_YESNOCANCEL);
if (IDYES == iResult) {
CreateNewXData();
}
else if (IDNO == iResult) {
CreateNewXData();
pLastNode->rbnext = pData->rbnext;
pData->rbnext = pExDataRb;
}
else {
return;
}
}
else
{
CreateNewXData();
}
spEnt->setXData(pExDataRb);
acutRelRb(pData);
}
仅需将已有扩展数据的最后一个数据的rbnext指针指向我们新添加的节点即可
删除扩展数据的接口如下:
virtual Mcad::ErrorStatus delXData(
LPCTSTR pzsAppName = NULL - 删除的扩展数据名称,如果为空,删除所有扩展数据。
);