Hello. I've been trying unsuccesfully to copy a layout and its viewports from a dwt/dwg file. I'll post some of the code, leaving out the failure checks, since none of the function calls failed in my tests. I know the functionality already exists, but we need an automated process for our purposes.
AcDbDatabase *pTempAcDbDatabase = new AcDbDatabase(false, true);
es = pTempAcDbDatabase->readDwgFile(szProjectName, _SH_DENYNO);
AcDbDictionary *pAcDbDictionary = acadGetLayoutDictionary(pTempAcDbDatabase);
I then retrieve the layout i am interested in, as an AcDbLayout*
AcDbObjectId layoutId,blockTableRecordId;
AcDbLayout *pAcDbLayout = NULL;
es = pLayoutMng->createLayout(szLayoutName,layoutId,blockTableRecordId); assert(es == Acad::eOk);
es = acdbOpenObject(pAcDbLayout,layoutId,AcDb::kForWrite); assert(es == Acad::eOk);
es = pAcDbLayout->copyFrom(pAcDbOrigLayout); assert(es == Acad::eOk);
es = pAcDbLayout->close();
pLayoutMng->updateLayoutTabs();
pLayoutMng->updateCurrentPaper();
es = pLayoutMng->setCurrentLayout(szLayoutName);
It works fine if i keep to just copying the layout
I have tried two different methods of copying the viewports, i'll describe the symptoms
Method 1
AcDbBlockTable *pTable;
AcDbBlockTableRecord *pPsBTR;
AcDbObjectId pPsBTRId;
if (Acad::eOk == acdbHostApplicationServices()->workingDatabase()->getBlockTable(pTable, AcDb::kForRead))
if (Acad::eOk == pTable->getAt(ACDB_PAPER_SPACE, pPsBTR, AcDb::kForWrite))
pPsBTRId = pPsBTR->objectId();
AcDbObjectIdArray viewportArray = pAcDbOrigLayout->getViewportArray();
viewportArray.removeAt(0);
AcDbIdMapping idMapping;
es = pTempAcDbDatabase->wblockCloneObjects(viewportArray, pPsBTRId, idMapping, AcDb::kDrcMangleName);
assert(es == Acad::eOk);
If i check with the GetViewportArray function for pAcDbLayout, it returns an array with the logical length of one, when i should have been five.
i then close the original layout, new layout and layoutdictionary.
If i delete the pTempDatabase, i get a crash "error handle re-entered, exiting now" after i try to double click one of the viewports. I can't see anything through the viewports,
If i don't delete the pTempDatabase, i don't get any crashes, double clicking on the viewports doesn't let me "enter" them, nor can i see anything through them.
Method 2 gets the same results basically
AcDbObjectIdArray viewportArray = pAcDbOrigLayout->getViewportArray();
for(int i=0; i<viewportArray.logicalLength(); i++)
{
AcDbViewport *pOldAcDbViewport;
if(acdbOpenObject(pOldAcDbViewport, viewportArray[i], AcDb::kForRead) == Acad::eOk)
{
AcDbViewport *pNewAcDbViewport = new AcDbViewport(/*pOldAcDbViewport*/);
if(pNewAcDbViewport->copyFrom(pOldAcDbViewport) == Acad::eOk)
{
AcDbBlockTableRecord *pBtr = NULL;
if(acdbOpenObject(pBtr, pPsBTRId, AcDb::kForWrite) == Acad::eOk)
es = pBtr->appendAcDbEntity(pNewAcDbViewport); assert(es == Acad::eOk);
es = pBtr->close(); assert(es == Acad::eOk);
es = pNewAcDbViewport->close(); assert(es == Acad::eOk);
}
es = pOldAcDbViewport->close(); assert(es == Acad::eOk);
}
}
Does this help http://adndevblog.typepad.com/autocad/2013/01/create-a-copy-of-a-layout-from-a-drawing-template.html
So here i am with an update. I'm not sure of anything anymore, the error seems somehow related to a corruption error i get when exiting the program manually, not via "stop debugging", meaning it only manifest itself after i get that error. So it's probably not this code, since i've had this error since before i started working on this function.
Try this...
1) In Visual Studio, select tools->options->debugging->symbols and add http://symbols.autodesk.com/symbols as a Symbol file (.pdb) location. Make sure All Modules are set to load at the bottom.
2) Open your application project in Visual Studio and start debugging... This make time some time because the debug pdb's are being downloaded from http://symbols.autodesk.com/symbols to your symbol temp folder.
3) Once AutoCAD has finished loading, go back to Visual Studio and select Debugging->Exceptions... turn on all VS exception handlers.
4) Run your program as normal... Does VS ping up with any exceptions? If so, check the Callstack in Visual Studio - anything strange?
5) Quit AutoCAD, does VS ping up with any exceptions? If so, check the Callstack in Visual Studio - anything strange? Pint the callstack here...
Sorry, i wasn't clear enough, the error when quitting the problem was on bricscad. So the new code still isn't working on brics, but that none of your concern.
In the new code, i used both the transaction manager and switching the current database, even though i would love a few pointer the when i should and when i should not switch the current database. Here's the code for a single layout copy. When i've got enought time i'll try and see wether it's the transaction manager or the changing of the current database that did the trick. Also, i'll try copying the viewportidarray directly.
AcDbDatabase *pOldWorkingDatabase = acdbHostApplicationServices()->workingDatabase(); AcDbTransactionManager *pTransaction = acdbHostApplicationServices()->workingDatabase()->transactionManager(); pTransaction->startTransaction(); AcDbObjectId layoutId,blockTableRecordId; AcDbLayout *pAcDbLayout = NULL; es = pLayoutMng->createLayout(szLayoutName,layoutId,blockTableRecordId,pOldWorkingDatabase); es = acdbOpenObject(pAcDbLayout,layoutId,AcDb::kForWrite); acdbHostApplicationServices()->setWorkingDatabase(pTempAcDbDatabase); es = pAcDbLayout->copyFrom(pAcDbOrigLayout); acdbHostApplicationServices()->setWorkingDatabase(pOldWorkingDatabase); es = pAcDbLayout->close(); es = pLayoutMng->setCurrentLayout(szLayoutName); AcDbObjectId id = pAcDbOrigLayout->getBlockTableRecordId(); AcDbBlockTableRecord *pBTR = NULL; es = acdbOpenObject(pBTR, id, AcDb::kForRead); AcDbBlockTableRecordIterator *pIter = NULL; es = pBTR->newIterator(pIter); AcDbObjectIdArray objIdArray; for(;!pIter->done();pIter->step()) { AcDbObjectId objectId; es = pIter->getEntityId(objectId); objIdArray.append(objectId); } delete pIter; es = pBTR->close(); if(objIdArray.logicalLength() > 0) { acdbHostApplicationServices()->setWorkingDatabase(pTempAcDbDatabase); AcDbIdMapping idMapping; es = pOldWorkingDatabase->wblockCloneObjects(objIdArray, pAcDbLayout->getBlockTableRecordId(), idMapping, AcDb::kDrcMangleName); acdbHostApplicationServices()->setWorkingDatabase(pOldWorkingDatabase); } es = pTransaction->endTransaction();
You are right. Setting the workingdatabase is required for copying layouts.
Setting the working database should be avoided if possible, as it can make AutoCAD unstable.
But there are few examples like the one you have come across that still need setting of the working database.
In such cases, the setting of the working database and resetting it back should be limited to a small portion of your code.
The other API that I am aware of which also needs the setting of the working database is the "adjustAlignment" method.