Hi all.
AutoCAD 2009 SP3 Enu;
Windows XP SP3 Rus;
MS Visual Studio 2010 Premium Rus;
.Net Framework 3.5 SP1;
In English comments of the code I have specified a problem: I can not save a database after change in the same file.
public static void MultipleAttSync(string sourceFile, string password, string[] blockNames, string[] targetFiles) {
if (!File.Exists(sourceFile))
throw new FileNotFoundException(sourceFile);
using (Database sourceDb = new Database(false, true)) {
sourceDb.ReadDwgFile(sourceFile, System.IO.FileShare.Read, true, password);
ObjectIdCollection blockIds = new ObjectIdCollection();
using (Transaction t = sourceDb.TransactionManager.StartTransaction()) {
// Открываем таблицу блоков
BlockTable bt = (BlockTable) t.GetObject(sourceDb.BlockTableId, OpenMode.ForRead, false);
// Извлекаем нужные определения блоков
foreach (BlockTableRecord btr in bt.Cast<ObjectId>().Select(n =>
(BlockTableRecord) t.GetObject(n, OpenMode.ForRead, false))
.Where(n => !n.IsAnonymous && !n.IsLayout && blockNames.Contains(n.Name))) {
blockIds.Add(btr.ObjectId);
btr.Dispose();
}
t.Commit();
}
try {
foreach (FileInfo item in targetFiles.Select(n => new FileInfo(n)).Where(n => n.Exists)) {
// Копируем определения блоков в нужную нам базу данных
IdMapping mapping = new IdMapping();
using (Database targetDb = new Database(false, true)) {
//Открываем целевую базу для изменений
targetDb.ReadDwgFile(sourceFile, FileShare.None, true, password);
sourceDb.WblockCloneObjects(blockIds, targetDb.BlockTableId, mapping, DuplicateRecordCloning.Replace, false);
//Теперь нужно выполнить синхронизацию вхождений блоков с их определениями
using (Transaction t = targetDb.TransactionManager.StartTransaction()) {
BlockTable bt = (BlockTable) t.GetObject(targetDb.BlockTableId, OpenMode.ForRead, false);
foreach (ObjectId id in bt) {
BlockTableRecord btr = (BlockTableRecord) t.GetObject(id, OpenMode.ForRead);
//Синхронизацию выполняем только для обновлённых определений блоков
if (blockNames.Select(m => m.ToLower()).Contains(btr.Name.ToLower()))
btr.AttSync(false, true, false);
}
t.Commit();
}
//targetDb.Save();//I get error: eFileInternalErr
//targetDb.SaveAs(targetDb.Filename, DwgVersion.Current);//I get message box: Error writing/closing file
targetDb.SaveAs(@"D:\dwg\drawings\Modified.dwg", DwgVersion.Current);//It work Successfully
}
}
}
catch (Autodesk.AutoCAD.Runtime.Exception ex) {
throw new System.Exception(string.Format("In the course of import there was an error: {0}", ex.Message));
}
}
}
If it is interesting to someone - I show two more methods (and one auxiliary) which can be useful to obtaining of the necessary drawings. I am sorry for Russian comments, but from titles of methods it should be clear that they do.
/// <summary>
/// Получить базы данных всех чертежей, листы которых входят в состав указанной подшивки
/// </summary>
/// <param name="sheetSetFileName">Анализируемая подшивка</param>
/// <returns>Возвращается перечисление найденных баз данных, листы которых используются в подшивке</returns>
public static FileInfo[] GetDrawingsFromSheetSet(string sheetSetFileName) {
//Проверяем наличие указанного файла
if (!File.Exists(sheetSetFileName))
throw new FileNotFoundException(sheetSetFileName);
//Проверяем, что расширение файла соответствует расширению файла подшивки
FileInfo file = new FileInfo(sheetSetFileName);
if (file.Extension.ToLower() != ".dst")
throw new System.Exception("Файл подшивки должен иметь расширение \".dst\"!");
//Получаем все листы подшивки
List<FileInfo> x = new List<FileInfo>();
//********************
// Получаем менеджера подшивки
AcSmSheetSetMgr mgr = new AcSmSheetSetMgr();
// Создаём новый объект базы данных подшивки
AcSmDatabase db = new AcSmDatabase();
// Пытаемся загрузить dst-файл
try {
db = mgr.OpenDatabase(sheetSetFileName, true);
}
catch (System.Exception ex) {
throw new System.Exception(string.Format(
"Не удалось открыть файл подшивки. Убедитесь, что он сейчас никем не используется. Сообщение: {0}", ex.Message));
}
try {
// На время блокируем базу данных подшивки от изменения извне
db.LockDb(db);
AcSmSheetSet ss = db.GetSheetSet();
ProcessEnumerator(ss.GetSheetEnumerator(), ref x);
}
catch (System.Exception ex) {
throw new System.Exception(string.Format(
"Не удалось заблокировать файл подшивки. Убедитесь, что он сейчас никем не используется. Сообщение: {0}", ex.Message));
}
//Снимаем блокировку базы данных подшивки
finally {
db.UnlockDb(db, true);
mgr.Close(db);
}
//Получаем базы данных чертежей, листы которых используются в подшивке
//Возвращаем результат обработки
return x.Select(n => n).Distinct().ToArray();
}
/// <summary>
/// Извлечение полных имён файлов, включенных в состав подшивки
/// </summary>
/// <param name="iter">Родительский объект</param>
/// <param name="x">ссылка на объект List<FileInfo>, в котором инкапсулирован результат</param>
static void ProcessEnumerator(IAcSmEnumComponent iter, ref List<FileInfo> x) {
IAcSmComponent item = iter.Next();
while (item != null) {
string type = item.GetTypeName();
switch (type) {
//Группа листов (рекурсивная обработка, т.к. может содержать вложенные группы листов)
case "AcSmSubset":
try {
AcSmSubset subset = (AcSmSubset) item;
string subName = subset.GetName();
if (!String.IsNullOrEmpty(subName)) {
IAcSmEnumComponent enumerator = (IAcSmEnumComponent) subset.GetSheetEnumerator();
ProcessEnumerator(enumerator, ref x);
}
}
catch { }
break;
//Лист
case "AcSmSheet":
try {
AcSmSheet sh = (AcSmSheet) item;
x.Add(new FileInfo(sh.GetLayout().GetFileName()));
if (!String.IsNullOrEmpty(sh.GetName())) {
IAcSmEnumComponent enumerator = (IAcSmEnumComponent) sh.GetSheetViews();
ProcessEnumerator(enumerator, ref x);
}
}
catch { }
break;
}
item = iter.Next();
}
}
/// <summary>
/// Получение всех чертежей, хранящихся в определённом каталоге
/// </summary>
/// <param name="directoryFullName">Полное имя каталога</param>
/// <param name="subDirsBrowse">Выполнять ли поиск по подкаталагам (True - выполнять; False - не выполнять)</param>
/// <returns>Возвращается последовательностьFileInfo</returns>
public static FileInfo[] GetDrawingsFromDirectory(string directoryFullName, bool subDirsBrowse) {
DirectoryInfo dir = new DirectoryInfo(directoryFullName);
if (!dir.Exists) throw new DirectoryNotFoundException();
return dir.GetFiles("*.dwg", subDirsBrowse ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)
.AsEnumerable().OrderBy(n => n.DirectoryName).ThenBy(n => n.Name).ThenBy(n => n.Extension).ToArray();
}
My test code:
[CommandMethod("MultipleAttSync", CommandFlags.Modal)]
public void MultipleAttSync() {
string[] blockNames = new string[] { "blockDef_1", "BlockDef_2", "BlockDef_3"};
string[] dwgFiles = AcEnvironment.GetDrawingsFromDirectory(@"D:\dwg\drawings", true).Select(n=>n.FullName).ToArray();
AcEnvironment.MultipleAttSync(@"D:\dwg\block definitions\Block definitions.dwg", "", blockNames, dwgFiles);
}
Why at me it is impossible to save changes in the same file?