问题描述: 

该问题非常隐蔽,很难查,场景:

在AssocDeepClone反应器中,维护了一个ZcDbAssocDeepCloneData的Data数组,每次克隆开始beginDeepClone时,都会往该数组插入一个Data,Data中记录了传入IdMap的地址,以及克隆过程中收集的数据。在clone过程中会往全局静态BTRPair数组记录源块定义和目标块定义的对,用于xlate时克隆相应块中的关联网络。

在endDeepClone时,会根据传入IdMap,从数组中移除相应的Data。

在abortDeepClone时,会直接清空该Data数组。

正常情况下,克隆开始插入一个Data,结束时删除,这样不会有问题。

和wblock/insert接口不同,wblockCloneObjects和deepCloneObjects支持deferXlation,默认情况下deferXlation为false,此时调用该接口能正常删除Data。

1.当用户想要为objects设置不同的owner时,可以将objects分组,多次调用cloneObjects接口,并且在调用时将deferXlation设置为true,仅在最后一次调用时设为false,去做xlate。如果客户调用该接口时,忘记在最后一次将deferXlation设为false,此时就有问题了

我们不知道该克隆是否结束,所以没法确定是否要清理Data数组,此时Data中记录的IdMap地址很可能已经变为野指针了。

2.多次调用时传入的AcDbIdMapping应该是同一个对象,如果写在一个循环里,每次调用这个接口之后AcDbIdMapping析构了导致崩溃,应该把这个对象提到循环外,这样符合文档上的用法(不这么做克隆的xlate应该会有一些正确性问题)。

总结:

克隆来自不同所有者的对象
如果要克隆来自不同所有者的一组对象,则需要将对象 ID 集划分为每个所有者的单独组。(克隆的对象及其所有者应属于同一数据库。在本节的示例中,将要克隆的模型空间对象添加到 中,将要克隆的图纸空间对象添加到:objListMS,objListPS:

objListMS.append(objId);
objListPS.append(objId);

然后,使用相同的 ID 映射调用该函数两次。必须使用单个 ID 映射进行所有克隆,才能正确完成参考翻译。在第一次调用时,该参数设置为 。在第二次(最后一次)调用时,默认为 :deepCloneObjects() deferXlation kTrue deepCloneObjects() deferXlation kFalse。

acdbHostApplicationServices()->workingDatabase()->
    deepCloneObjects(mslist, modelSpaceId, idMap, Adesk::kTrue);
acdbHostApplicationServices()->workingDatabase()->
    deepCloneObjects(pslist, paperSpaceId, idMap);


以下代码深度克隆属于不同所有者的对象示例:

void
cloneDiffOwnerObjects()
{
    // Step 1: Obtain the set of objects to be cloned.
    // For the two owners we'll use model space and
    // paper space, so we must perform two acedSSGet() calls.
    // calls.
    //
    acutPrintf("\nSelect entities to be cloned to"
        " Model Space");
    ads_name ssetMS;
    acedSSGet(NULL, NULL, NULL, NULL, ssetMS);
    long lengthMS;
    acedSSLength(ssetMS, &lengthMS);
    acutPrintf("\nSelect entities to be cloned to"
        " Paper Space");
    ads_name ssetPS;
    if (acedSSGet(NULL, NULL, NULL, NULL, ssetPS)
        != RTNORM && lengthMS == 0)
    {
        acutPrintf("\nNothing selected");
        return;
    }
    long lengthPS;
    acedSSLength(ssetPS, &lengthPS);
    // Step 2: Add obtained object IDs to the lists of
    // objects to be cloned: one list for objects to
    // be owned by model space and one for those to
    // be owned by paper space.
    AcDbObjectId ownerId = AcDbObjectId::kNull;
    // For model space
    //
    AcDbObjectIdArray  objListMS;
    for (int i = 0; i < lengthMS; i++) {
        ads_name ent;
        acedSSName(ssetMS, i, ent);
        AcDbObjectId objId;
        acdbGetObjectId(objId, ent);
        // Check to be sure this has the same owner as the first
        // object.
        //
        AcDbObject *pObj;
        acdbOpenObject(pObj, objId, AcDb::kForRead);
        if (pObj->ownerId() == ownerId)
            objListMS.append(objId);
        else if (i == 0) {
            ownerId = pObj->ownerId();
            objListMS.append(objId);
        }
        pObj->close();
    }
    acedSSFree(ssetMS);
    // For paper space
    //
    ownerId = AcDbObjectId::kNull;
    AcDbObjectIdArray  objListPS;
    for (i = 0; i < lengthPS; i++) {
        ads_name ent;
        acedSSName(ssetPS, i, ent);
        AcDbObjectId objId;
        acdbGetObjectId(objId, ent);
        // Check to be sure this has the same owner as the first
        // object.
        //
        AcDbObject *pObj;
        acdbOpenObject(pObj, objId, AcDb::kForRead);
        if (pObj->ownerId() == ownerId)
            objListPS.append(objId);
        else if (i == 0) {
            ownerId = pObj->ownerId();
            objListPS.append(objId);
        }
        pObj->close();
    }
    acedSSFree(ssetPS);
    // Step 3: Get the object ID of the desired owners for
    // the cloned objects.  We're using model space and
    // paper space for this example.
    //
    AcDbBlockTable *pBlockTable;
    acdbHostApplicationServices()->workingDatabase()
        ->getSymbolTable(pBlockTable, AcDb::kForRead);
    AcDbObjectId  modelSpaceId, paperSpaceId;
    pBlockTable->getAt(ACDB_MODEL_SPACE, modelSpaceId);
    pBlockTable->getAt(ACDB_PAPER_SPACE, paperSpaceId);
    pBlockTable->close();
    // Step 4:  Create a new ID map.
    //
    AcDbIdMapping idMap;
    // Step 5: Call deepCloneObjects().
    //
    acdbHostApplicationServices()->workingDatabase()
        ->deepCloneObjects(objListMS, modelSpaceId, idMap,
        Adesk::kTrue);
    acdbHostApplicationServices()->workingDatabase()
        ->deepCloneObjects(objListPS, paperSpaceId, idMap);
    // Now we can go through the ID map and do whatever we'd
    // like to the original and/or clone objects.
    //
    // For this example we'll print out the object IDs of
    // the new objects resulting from the cloning process.
    //
    AcDbIdMappingIter iter(idMap);
    for (iter.start(); !iter.done(); iter.next()) {
        AcDbIdPair idPair;
        iter.getMap(idPair);
        if (!idPair.isCloned())
            continue;
        acutPrintf("\nObjectId is: %Ld",
            idPair.value().asOldId());
    }
}


英文空间链接


★中望CAD中ZRX开发wblockCloneObjects使用不当造成崩溃如何处理?https://confluence.zwcad.com/pages/viewpage.action?pageId=298355923

  • No labels