Ошибка eSetFailed при установке текущего листа в AutoCAD 2016

Автор Тема: Ошибка eSetFailed при установке текущего листа в AutoCAD 2016  (Прочитано 10617 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн Александр Пекшев aka ModisАвтор темы

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
Всем привет!

Имеется такой кусочек кода:
Код - C# [Выбрать]
  1. private ObjectId CreateLayout(Layout importLayout, string newLayoutName)
  2. {
  3.     var curDoc = AcApp.DocumentManager.MdiActiveDocument;
  4.     var curDb = curDoc.Database;
  5.  
  6.     using var curT = curDb.TransactionManager.StartTransaction();
  7.  
  8.     var layoutName = ReplaceSymbols(newLayoutName);
  9.     var layoutDic = curDb.LayoutDictionaryId.GetObjectAs<DBDictionary>();
  10.     var index = 0;
  11.  
  12.     while (layoutDic.Contains(layoutName))
  13.         layoutName = $"{newLayoutName} {++index}";
  14.  
  15.     var newLayoutId = LayoutManager.Current.CreateLayout(layoutName);
  16.     var newLayout = newLayoutId.GetObjectAs<Layout>(true);
  17.     newLayout.CopyFrom(importLayout);
  18.     LayoutManager.Current.CurrentLayout = newLayout.LayoutName;
  19.     foreach (ObjectId id in newLayout.GetViewports())
  20.         id.GetObjectAs<Viewport>(true).Erase(true);
  21.  
  22.     curT.Commit();
  23.  
  24.     return newLayoutId;
  25. }

На 18 строчке в 2016 автокаде выскакивает ошибка eSetFailed. При этом имя для листа корректное и в 15 строчке я получаю идентификатор созданного листа.
Если после создания листа (после 15 строчки) вызвать
Код - C# [Выбрать]
  1. LayoutManager.Current.GetLayoutId(layoutName)
то вернет id = 0
Если вместо 18 строчки написать так
Код - C# [Выбрать]
  1. LayoutManager.Current.SetCurrentLayoutId(newLayoutId);
то тоже будет ошибка.

На автокад установлены все хотфиксы. Проблема известна только в 2016 версии (проверено на 2013, 2016, 2022, 2023). Может и еще в каких-то соседних версиях имеется, не знаю.

У меня конечно есть мысль, что это баг 2016 версии, но вдруг я просто не знаю какого-то хака или нюанса

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13880
  • Карма: 1786
  • Рыцарь ObjectARX
  • Skype: rivilis
Код выполняется в контексте приложения или в контексте документа?
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Александр Пекшев aka ModisАвтор темы

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
Код выполняется в контексте приложения или в контексте документа?
В контексте документа. При работе конечно используется окно с прогрессом, но оно сделано в отдельном потоке.
Если я правильно понял вопрос )

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13880
  • Карма: 1786
  • Рыцарь ObjectARX
  • Skype: rivilis
В контексте документа.
1. Попробуй в контексте приложения (Session)
2. Попробуй LayoutManager.Current.CurrentLayout = newLayout.LayoutName; после завершения транзакции (вне транзакции).
Лучше LayoutManager.Current.SetCurrentLayoutId(newLayoutId);
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Александр Пекшев aka ModisАвтор темы

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
В контексте документа.
1. Попробуй в контексте приложения (Session)
2. Попробуй LayoutManager.Current.CurrentLayout = newLayout.LayoutName; после завершения транзакции (вне транзакции).
Лучше LayoutManager.Current.SetCurrentLayoutId(newLayoutId);
1. Не помогло
2. Не помогло

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13880
  • Карма: 1786
  • Рыцарь ObjectARX
  • Skype: rivilis
2. Не помогло
Подозреваю, что это проблема с твоей стороны. Про такой баг в AutoCAD 2016 неизвестно. Были варианты, когда нужно делить транзакцию на две.
Покажи вариант программы 2 с использованием только методов, доступных в AutoCAD .NET API (без твоих расширений).
Судя по коду у тебя вложенные транзакции.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Александр Пекшев aka ModisАвтор темы

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
2. Не помогло
Подозреваю, что это проблема с твоей стороны. Про такой баг в AutoCAD 2016 неизвестно. Были варианты, когда нужно делить транзакцию на две.
Покажи вариант программы 2 с использованием только методов, доступных в AutoCAD .NET API (без твоих расширений).
Судя по коду у тебя вложенные транзакции.

Да, проблема где-то в коде, но я пока не могу понять где. Попробовал создание листа в самом начале команды - и никаких ошибок.

Сам код плагина тут
Расширения тут

Вложенных транзакций нет

Можно конечно попробовать переписать без расширений, но странно, что в других версиях автокада работает

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13880
  • Карма: 1786
  • Рыцарь ObjectARX
  • Skype: rivilis
Александр Пекшев aka Modis,
Хм. Ты думаешь я буду заниматься исследованием всего кода твоего плагина? ;)
Я предположил, что у тебя вложенные транзакции так как ты в код метода CreateLayout передаешь Layout, т.е. он уже где-то открыт в другой транзакции. Надеюсь, что та транзакция не закрыта до того, как ты передаешь этот Layout. Если же он открыт не в транзакции, то у тебя смесь транзакция/нетранзакция, что тоже нехорошо.
« Последнее редактирование: 31-08-2022, 22:02:38 от Александр Ривилис »
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Александр Пекшев aka ModisАвтор темы

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
Александр Ривилис, ну вдруг вы (или кто-то другой) захотели бы посмотреть. Лучше с кодом, чем без =)

Но вернемся к коду - переписал без расширений. Ошибка на месте.

Метод выше:
Код - C# [Выбрать]
  1. public void ImportSheets(string dwgFile, string commonNamePart, ref double maxX)
  2. {
  3.     // Открытие чертежа с импортируемыми листами
  4.     using (var importDb = new Database(false, true))
  5.     {
  6.         importDb.CloseInput(true);
  7.         importDb.ReadDwgFile(dwgFile, FileShare.ReadWrite, true, string.Empty);
  8.  
  9.         using (var importTr = importDb.TransactionManager.StartTransaction())
  10.         {
  11.             // Копирование модели импортируемого чертежа в текущий со сдвигом в стартовую точку
  12.             _copyModelSpaceService.Copy(importDb, importTr, ref maxX, out var move, out var isEmptyModelSpace);
  13.  
  14.             // Копирование импортируемых листов
  15.            
  16.             using var layoutDic = (DBDictionary)importTr.GetObject(importDb.LayoutDictionaryId, OpenMode.ForRead);
  17.             foreach (var importLayoutItem in layoutDic)
  18.             {
  19.                 using (var importLayout = (Layout)importTr.GetObject(importLayoutItem.Value, OpenMode.ForRead))
  20.                 {
  21.                     if (importLayout.ModelType)
  22.                         continue;
  23.  
  24.                     using (var importLayoutBtr = (BlockTableRecord)importTr.GetObject(importLayout.BlockTableRecordId, OpenMode.ForRead))
  25.                     {
  26.                         var newLayoutName = Path.GetFileNameWithoutExtension(dwgFile);
  27.                         if (!string.IsNullOrEmpty(commonNamePart))
  28.                             newLayoutName = newLayoutName.Replace(commonNamePart, string.Empty);
  29.                         if (string.IsNullOrEmpty(newLayoutName))
  30.                             newLayoutName = Path.GetFileNameWithoutExtension(dwgFile);
  31.                        
  32.                         _copyLayoutService.Copy(importLayout, importLayoutBtr, importTr, newLayoutName, move, isEmptyModelSpace);
  33.                        
  34.                         break;
  35.                     }
  36.                 }
  37.             }
  38.  
  39.             importTr.Commit();
  40.         }
  41.     }
  42. }

В 32 строке сюда:
Код - C# [Выбрать]
  1. public void Copy(Layout importLayout, BlockTableRecord importLayoutBtr, Transaction importTr, string newLayoutName, Vector3d move, bool isEmptyModelSpace)
  2. {
  3.     var newLayoutId = CreateLayout(importLayout, newLayoutName);
  4.  
  5.     CopyContent(newLayoutId, importLayoutBtr, importTr, move, isEmptyModelSpace);
  6. }
  7.  

И сюда:
Код - C# [Выбрать]
  1. private ObjectId CreateLayout(Layout importLayout, string newLayoutName)
  2. {
  3.     var curDoc = AcApp.DocumentManager.MdiActiveDocument;
  4.     var curDb = curDoc.Database;
  5.  
  6.     ObjectId newLayoutId;
  7.     using (var curT = curDb.TransactionManager.StartTransaction())
  8.     {
  9.         var layoutName = ReplaceSymbols(newLayoutName);
  10.         var layoutDic = (DBDictionary)curT.GetObject(curDb.LayoutDictionaryId, OpenMode.ForRead);
  11.         var index = 0;
  12.  
  13.         while (layoutDic.Contains(layoutName))
  14.             layoutName = $"{newLayoutName} {++index}";
  15.  
  16.         newLayoutId = LayoutManager.Current.CreateLayout(layoutName);
  17.         var newLayout = (Layout)curT.GetObject(newLayoutId, OpenMode.ForWrite);
  18.         newLayout.CopyFrom(importLayout);
  19.         LayoutManager.Current.CurrentLayout = newLayout.LayoutName;
  20.         foreach (ObjectId id in newLayout.GetViewports())
  21.         {
  22.             curT.GetObject(id, OpenMode.ForWrite).Erase(true);
  23.         }
  24.  
  25.         curT.Commit();
  26.     }
  27.  
  28.     return newLayoutId;
  29. }

Оффлайн Привалов Дмитрий

  • ADN Club
  • *****
  • Сообщений: 546
  • Карма: 119
На 18 строчке в 2016 автокаде выскакивает ошибка eSetFailed. При этом имя для листа корректное и в 15 строчке я получаю идентификатор созданного листа.
В начальном коде у тебя скорее всего путаница с открытием/закрытием объектов.

1. using var curT = curDb.TransactionManager.StartTransaction(); а где вызов curT.Dispose(), после curT.Commit();?
но причина не в этом.

2. var layoutDic = curDb.LayoutDictionaryId.GetObjectAs<DBDictionary>();
   var newLayout = newLayoutId.GetObjectAs<Layout>(true);
ты открыл объект на чтение, а второй на запись вне транзакции, через id.Open()

скорее всего метод var newLayoutId = LayoutManager.Current.CreateLayout(layoutName); вернул id, но ожидает, когда ты вернешь ему layoutDic

соответственно попробуй для начала заменить
var layoutDic = curT.GetObjectAs<DBDictionary>(curDb.LayoutDictionaryId);
var newLayout = curT.GetObjectAs<Layout>(newLayoutId, true);
и добавить Dispose() для транзакции.

если не поможет, попробуй
var layoutDic = curT.GetObjectAs<DBDictionary>(curDb.LayoutDictionaryId, true);

Если не поможет, попробуй после var newLayoutId = LayoutManager.Current.CreateLayout(layoutName);
закрыть транакцию curT.Commit(); curT.Dispose();
и проверить:
LayoutManager.Current.GetLayoutId(layoutName)
LayoutManager.Current.SetCurrentLayoutId(newLayoutId);

если не поможет ищи в коде где еще открывал объекты через  Id.GetObjectAs и закрывай


Оффлайн Александр Пекшев aka ModisАвтор темы

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
Привалов Дмитрий, вы мне ответ написали к топику, хотя выше пример уже переписанного кода, где все открыто через транзакции и без расширений. Ну и самое главное из топика - код работает в 2013, 2022 и 2023 автокаде. Не работает в 2016. Остальные не проверял

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13880
  • Карма: 1786
  • Рыцарь ObjectARX
  • Skype: rivilis
Ну и самое главное из топика - код работает в 2013, 2022 и 2023 автокаде.
Ты же прекрасно знаешь, что это не показатель того, что ошибка в AutoCAD, а не в твоем коде. Тем более что:
Да, проблема где-то в коде, но я пока не могу понять где. Попробовал создание листа в самом начале команды - и никаких ошибок.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Александр Пекшев aka ModisАвтор темы

  • ADN Club
  • *****
  • Сообщений: 1658
  • Карма: 366
  • Отец modplus.org
    • ModPlus
Ну и самое главное из топика - код работает в 2013, 2022 и 2023 автокаде.
Ты же прекрасно знаешь, что это не показатель того, что ошибка в AutoCAD, а не в твоем коде. Тем более что:
Да, проблема где-то в коде, но я пока не могу понять где. Попробовал создание листа в самом начале команды - и никаких ошибок.
Так я и не говорю, что прям автокад виноват - я говорю, что проблема в коде не столь очевидная, что в одной версии не работает, а в других работает

Оффлайн Привалов Дмитрий

  • ADN Club
  • *****
  • Сообщений: 546
  • Карма: 119
Привалов ДмитрийНу и самое главное из топика - код работает в 2013, 2022 и 2023 автокаде. Не работает в 2016
Самое главное, что это не главное.

Не во всех версиях срабатывает куча кода в одной транзакции, на создание объекта, копирование свойств, редактирование.
Иногда нужно разделить сначала создать и закрыть, потом снова открыть и скопировать.

Попробуй упростить пример. В 1 транзакции считай имена Layout в List<string> , закрой транзакцию, проверь на всякий случай имена

Затем проверь функцию CreateLayout(), без newLayout.CopyFrom(importLayout);
и без
foreach (ObjectId id in newLayout.GetViewports())
{
    curT.GetObject(id, OpenMode.ForWrite).Erase(true);
}

Если все пройдет, можно тестировать дальше.
Если не сработает, нужно проанализировать в чем проблема.

...Еще странно, что ты удаляешь главный(первый) Viewport на листе.