項目總體規劃
考慮到項目需求,決定采用AutoCAD中的Accoreconsole.exe+.Net Dll來實作,對Accoreconsole不太了解的同學可以自行百度,總之一句話,要想高效的批量處理dwg檔案,這個AutoCAD控制台是必須要了解的。
對AutoCAD進行批量處理,以愚人之見,就是如下三個步驟:
- 用C#開發.net DLL程式,生成所有需要的AutoCAD指令(Command)。
- 編寫SCR指令檔案,在SCR中使用netload加載以上DLL并處理需要的AutoCAD指令(Command)。
- 用VBS捏合所有關于關于window下檔案的處理(很遺憾,本人來自工控行業,目前隻考慮windows平台,MAC、Linux暫不考慮)并調用Accoreconsole.exe,處理第2步中的SCR指令檔案。
項目的具體實施
1. 開發.net DLL程式
一、開發塊引入程式
經過對項目需求的進一步分析,本人覺得最佳的方法就是建立一個AutoCAD檔案,在此檔案中建立兩個塊DTL-L和DTL-R,分别對應即将被替換掉的TCDN()-L, TCDN()-R, 其中()内可能為任意字元。
下圖是建立好的dwg檔案,命名為StandardBlock.dwg

建立好這個StandardBlock.dwg後,我們需要做的第一步就是在每一個待修改的圖紙中,執行一個指令,将StandardBlock.dwg中的這兩個塊(DTL-L和DTL-R)引入到目前圖紙中,然後進行替換。我們先來開發如何實作從另一份圖紙中拷貝塊的資訊到目前圖紙。
1)建立.net framework 類庫項目
本人使用VS2019,其他版本類似
2)命名類庫名稱并更改.NET Framework 版本
類庫項目名稱命名為CADSmart, 注意,.NET Framework架構版本很重要,太低的話有可能會導緻與AutoCAD的DLL不相容。這裡我選用.NET Framework 4.6用來相容AutoCAD 2018
3)添加AutoCAD 庫的引用
在AutoCAD的安裝目錄中找到如下DLL并添加到項目引用
4)建立Utility類并添加靜态方法
建立此類的目的是将所有關于AutoCAD的操作封裝成靜态方法,進而在CADSmart.cs中進行調用。
static public void ImportBlocksFrmDwg(this Database desdb, string sourceFileName)
{
Database sourceDb = new Database(false, true);
try
{
sourceDb.ReadDwgFile(sourceFileName, System.IO.FileShare.Read, true, null);
ObjectIdCollection blocks = new ObjectIdCollection();
Autodesk.AutoCAD.DatabaseServices.TransactionManager tranm = sourceDb.TransactionManager;
using (Transaction tran = tranm.StartTransaction())
{
BlockTable bt = (BlockTable)tran.GetObject(sourceDb.BlockTableId, OpenMode.ForRead, false);
foreach (ObjectId btrId in bt)
{
BlockTableRecord btr = tranm.GetObject(btrId, OpenMode.ForRead, false) as BlockTableRecord;
//隻加入命名塊和非布局塊到複制清單中
if (!btr.IsAnonymous && !btr.IsLayout)
{
blocks.Add(btrId);
}
btr.Dispose();
}
bt.Dispose();
}
IdMapping mapping = new IdMapping();
sourceDb.WblockCloneObjects(blocks, desdb.BlockTableId, mapping, DuplicateRecordCloning.Replace, false);
}
catch (Autodesk.AutoCAD.Runtime.Exception ex)
{
Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog("錯誤資訊" + ex.Message);
}
//操作完成,銷毀源資料庫
sourceDb.Dispose();
}
二、開發塊替換程式
同樣在Utility.cs中添加如下靜态方法,使用引入的塊進行塊的替換,首先得到原先塊參照的EID,儲存在strEID中,并儲存原塊參照的插入位置(oldLocation)和比例(oldScale),之後删除此塊參照,并根據此塊參照的類型,決定是插入DTL-L還是DTL-R,插入塊後,更新塊屬性,将strEID指派給塊屬性。
static public void ReplaceBlock(this Database db)
{
using (var tr = db.TransactionManager.StartTransaction())
{
var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
// check if the block table contains old block
List<ObjectId> listId = new List<ObjectId>();
foreach (ObjectId id in bt)
{
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(id, OpenMode.ForRead, false);
if (btr.Name.Length!=7)
{
continue;
}
if(btr.Name.Substring(0,4)=="TCDN" && (btr.Name.Substring(5,2)=="-L" || btr.Name.Substring(5, 2) == "-R"))
{
listId.Add(id);
}
}
// check if the block table contains onew block
ObjectId newBlockIdL = new ObjectId();
ObjectId newBlockIdR = new ObjectId();
if (bt.Has("DTL-L"))
{
newBlockIdL = bt["DTL-L"];
}
if (bt.Has("DTL-R"))
{
newBlockIdR = bt["DTL-R"];
}
foreach (ObjectId objectId in listId)
{
// replace all references to old block by references to new block
var oldBtr = (BlockTableRecord)tr.GetObject(objectId, OpenMode.ForRead);
foreach (ObjectId id in oldBtr.GetBlockReferenceIds(true, true))
{
var br = (BlockReference)tr.GetObject(id, OpenMode.ForWrite);
AttributeCollection attCol = br.AttributeCollection;
string strEID = string.Empty;
foreach (ObjectId attId in attCol)
{
AttributeReference attRef = (AttributeReference)tr.GetObject(attId, OpenMode.ForWrite);
if (attRef.Tag == "EID")
{
strEID = attRef.TextString;
break;
}
}
Scale3d oldScale = br.ScaleFactors;
Point3d oldLocation = br.Position;
br.Erase();
//BlockReference newBR;
ObjectId myBlockId = new ObjectId();
string blkName = string.Empty;
if (oldBtr.Name.Substring(0, 4) == "TCDN" && oldBtr.Name.Substring(5, 2) == "-L")
{
myBlockId = newBlockIdL;
blkName = "DTL-L";
}
else
{
myBlockId = newBlockIdR;
blkName = "DTL-R";
}
BlockTableRecord ms = bt[BlockTableRecord.ModelSpace].GetObject(OpenMode.ForWrite) as BlockTableRecord;
BlockTableRecord blockDef = bt[blkName].GetObject(OpenMode.ForRead) as BlockTableRecord;
using (BlockReference blockRef = new BlockReference(oldLocation, blockDef.ObjectId))
{
//Add the block reference to modelspace
blockRef.ScaleFactors = oldScale;
ms.AppendEntity(blockRef);
tr.AddNewlyCreatedDBObject(blockRef, true);
foreach (ObjectId myid in blockDef)
{
DBObject obj = myid.GetObject(OpenMode.ForRead);
AttributeDefinition attDef = obj as AttributeDefinition;
if ((attDef != null) && (!attDef.Constant))
{
using (AttributeReference attRef = new AttributeReference())
{
attRef.SetAttributeFromBlock(attDef, blockRef.BlockTransform);
attRef.TextString = strEID;
blockRef.AttributeCollection.AppendAttribute(attRef);
tr.AddNewlyCreatedDBObject(attRef, true);
}
}
}
}
}
}
tr.Commit();
}
}