作为一名在CAD领域深耕多年的开发者,我最近完成了中望CAD 2024平台的二次开发项目。这个项目主要基于ZRX(中望CAD Runtime Extension)框架,使用C++语言实现了扩展记录功能。简单来说,就是让中望CAD能够识别和处理自定义的数据结构,并将这些数据完美集成到DWG文件中。
在实际工程应用中,我们经常需要在CAD图纸中存储一些非图形数据,比如材料属性、施工工艺说明或者成本信息。传统做法是用文字标注或者外部数据库,但这些方案都存在明显缺陷。通过ZRX扩展记录功能,我们可以直接在DWG文件中嵌入结构化数据,既保证了数据完整性,又方便后续程序化处理。
中望CAD 2024的二次开发需要准备以下核心工具:
重要提示:安装路径不要包含中文或特殊字符,否则可能导致编译异常。我习惯将所有开发工具安装在D:\DevTools这样的纯英文路径下。
新建Win32 DLL项目后,需要配置以下关键属性:
cpp复制// 示例:预处理器定义
#define ZWCAD2024
#define _ZWCAD_ZRX
#define _ARX_ZRX // 兼容ARX接口
扩展记录本质上是一个键值对容器,但比普通字典更强大。它支持以下数据类型:
cpp复制// 典型扩展记录定义
class MyCustomData : public ZcRxObject {
public:
ZCRX_DECLARE_MEMBERS(MyCustomData);
// 字段定义
std::string materialName;
double thickness;
int fireRating;
// 序列化方法
virtual Zcad::ErrorStatus dwgInFields(ZcDbDwgFiler* filer) override;
virtual Zcad::ErrorStatus dwgOutFields(ZcDbDwgFiler* filer) const override;
};
要让中望CAD识别自定义数据,必须实现完整的注册流程:
cpp复制// 初始化函数示例
void initApp() {
// 注册自定义类
MyCustomData::rxInit();
zcrxRegisterClass(MyCustomData::desc(),
MyCustomData::desc()->name(),
MyCustomData::desc()->dxfName());
// 添加命令
zcrxDefineCommand("ADDMYDATA", addMyData,
ZCRX_CMD_MODAL | ZCRX_CMD_USEPICKSET);
}
扩展记录最常用的场景是将数据附加到CAD实体上:
cpp复制void attachDataToEntity(const ZcDbObjectId& entId) {
// 打开实体
ZcDbEntityPointer pEnt(entId, ZcDb::kForWrite);
// 创建扩展记录
MyCustomData* pData = new MyCustomData();
pData->setMaterialName("Q235B");
pData->setThickness(12.5);
// 附加到实体
pEnt->xData()->append(pData);
pData->close();
}
通过扩展字典可以检索已有数据:
cpp复制void queryEntityData(const ZcDbObjectId& entId) {
ZcDbEntityPointer pEnt(entId, ZcDb::kForRead);
// 遍历扩展记录
ZcDbXdataPointer xdata = pEnt->xData();
for(int i=0; i<xdata->count(); i++) {
MyCustomData* pData = dynamic_cast<MyCustomData*>(xdata->at(i));
if(pData) {
acutPrintf("\nMaterial: %s", pData->materialName().c_str());
}
}
}
常见错误:在未打开事务的情况下直接修改实体属性,会导致程序崩溃。
实现多版本兼容的三种策略:
cpp复制#if defined(ZWCAD2024)
// 2024特有API
#elif defined(ZWCAD2023)
// 2023兼容代码
#endif
cpp复制int ver = zcrxGetHostVersion();
if(ver >= 20240000) {
// 使用新特性
}
cpp复制class IDataAdapter {
public:
virtual void saveData(ZcDbEntity* pEnt) = 0;
};
// 为每个版本实现具体适配器
对于频繁访问的数据,建议实现缓存:
cpp复制class DataCache {
static std::map<ZcDbObjectId, MyCustomData*> cache;
public:
static MyCustomData* get(ZcDbObjectId id) {
auto it = cache.find(id);
if(it != cache.end()) return it->second;
// 从数据库加载
MyCustomData* pData = loadFromDb(id);
cache[id] = pData;
return pData;
}
};
处理大量实体时,使用以下模式提升效率:
cpp复制void batchUpdate() {
ZcDbDatabase* pDb = zcdbHostApplicationServices()->workingDatabase();
ZcDbTransactionManager* pTm = pDb->transactionManager();
pTm->startTransaction();
{
// 禁用显示更新
zcrxDisableGraphicsUpdate(true);
// 批量处理
for(auto& id : entityIds) {
// 处理逻辑
}
zcrxDisableGraphicsUpdate(false);
}
pTm->endTransaction();
}
| 错误码 | 含义 | 解决方案 |
|---|---|---|
| eOk (0) | 成功 | - |
| eNullHandle | 空句柄 | 检查对象是否有效 |
| eNotOpenForWrite | 只读状态 | 确保以写模式打开 |
| eWrongObjectType | 类型不匹配 | 检查dynamic_cast结果 |
cpp复制void safeOperation(ZcDbEntity* pEnt) {
try {
// 可能失败的操作
} catch(const Zcad::ErrorStatus& es) {
acutPrintf("\n操作失败: %s", es.description().c_str());
zcrxBreak(); // 触发调试中断
}
}
在某钢结构项目中,我们使用扩展记录实现了:
关键实现代码:
cpp复制struct SteelMemberData : public ZcRxObject {
ZCRX_DECLARE_MEMBERS(SteelMemberData);
// 工程字段
string memberId;
string sectionType;
vector<WeldSymbol> welds;
// 序列化方法
ErrorStatus dwgInFields(ZcDbDwgFiler* filer) override {
// 实现略
}
};
在MEP系统中扩展记录用于存储:
cpp复制void addPipeData(ZcDbEntity* pPipe) {
PipeData* pData = new PipeData();
pData->setDiameter(150);
pData->setFlowRate(2.5);
pData->setInsulationThickness(30);
pPipe->xData()->append(pData);
pData->close();
}
更高级的方案是创建全新实体类型:
cpp复制class MySmartEntity : public ZcDbEntity {
public:
ZCRX_DECLARE_MEMBERS(MySmartEntity);
// 必须实现的方法
virtual Adesk::Boolean worldDraw(ZcGiWorldDraw* wd) override;
virtual Zcad::ErrorStatus dwgInFields(ZcDbDwgFiler* filer) override;
virtual Zcad::ErrorStatus dwgOutFields(ZcDbDwgFiler* filer) const override;
// 自定义方法
void updateGeometry();
};
通过反应器实现数据联动:
cpp复制class DataReactor : public ZcDbObjectReactor {
public:
virtual void modified(const ZcDbObject* dbObj) override {
// 当关联对象修改时触发
}
};
// 注册反应器
void addReactorToEntity(ZcDbObjectId id) {
DataReactor* pReactor = new DataReactor();
zcrxRegisterReactor(pReactor);
pReactor->addPersistentReactor(id);
}
建议:即使只有一个ZRX文件,也建议制作安装程序处理路径注册等问题。
推荐采用语义化版本控制:
示例版本定义:
cpp复制#define MYAPP_VERSION_MAJOR 2
#define MYAPP_VERSION_MINOR 1
#define MYAPP_VERSION_PATCH 3
const char* getVersionString() {
static char ver[32];
sprintf(ver, "%d.%d.%d",
MYAPP_VERSION_MAJOR,
MYAPP_VERSION_MINOR,
MYAPP_VERSION_PATCH);
return ver;
}
在长期使用中,我发现中望CAD的ZRX开发框架虽然学习曲线较陡,但一旦掌握就能实现非常强大的功能扩展。特别是在处理工程数据关联方面,扩展记录方案比传统的外部数据库方案更加稳定可靠。建议新手从简单的数据附着功能开始,逐步过渡到自定义实体开发。