Ошибка при использовании WblockCloneObjects

Автор Тема: Ошибка при использовании WblockCloneObjects  (Прочитано 31013 раз)

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

Оффлайн Volody1983Автор темы

  • ADN OPEN
  • **
  • Сообщений: 52
  • Карма: 0
Re: Ошибка при использовании WblockCloneObjects
« Ответ #30 : 09-02-2016, 18:57:40 »
Еще вопрос по поводу к какой базе все-таки применять метод WblockCloneObjects. Вот например здесь пишут так http://through-the-interface.typepad.com/through_the_interface/2006/08/breaking_it_dow.html. А вот здесь по другому http://adndevblog.typepad.com/autocad/2012/05/insert-block-from-a-different-dwg-using-net-.html . Из документации все ясно кроме как к какой базе правильно применять метод. У меня работают оба варианта.

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Ошибка при использовании WblockCloneObjects
« Ответ #31 : 09-02-2016, 23:13:52 »
Еще вопрос по поводу к какой базе все-таки применять метод WblockCloneObjects.
Теоретически должно работать и так, и так. Но так как копируется не только сама запись таблицы блоков, но и все связанные с ней объекты, правильнее указывать ту базу, в которую будет производится копирование. Я сталкивался с ошибками, которые возникали из-за неправильного указания базы.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN Club
  • *****
  • Сообщений: 546
  • Карма: 119
Re: Ошибка при использовании WblockCloneObjects
« Ответ #32 : 10-02-2016, 10:02:24 »
Вообщем выяснилось следующее убрал файл с блоками с виртуального диска (который создается командой subst) на локальный и все заработало как часы.
SourceDb.ReadDwgFile не загружает файл в память. А делает отложенную загрузку по требованию.
Поэтому нужно лезть в тонкости виртуальных дисков. Вдруг во время работы отваливается, меняет права доступа или не постоянен в памяти. Последнее сделает виртуальный диск полностью не пригодным для работы с ReadDwgFile. Как вариант можешь попробовать открыть файл, для записи, тогда может быть проблема уйдет.

Все-таки где-то сидит косяк. После вставки сотни блоков вылезла такая же ошибка. Но похоже ошибка все-таки где-то ниже
ReadDwgFile обращается к RealDwg, который является потоко не безопасным, а ты вызываешь его 100 раз. Постарайся минимизировать количество вызовов ReadDwgFile в коде, например подгрузить все нужные блоки сразу в чертеж, а не по мере необходимости.

Но возможно, как подсказал Александр причина порчи памяти в другой части кода, а WblockCloneObjects просто на нее наталкивается.

Оффлайн Volody1983Автор темы

  • ADN OPEN
  • **
  • Сообщений: 52
  • Карма: 0
Re: Ошибка при использовании WblockCloneObjects
« Ответ #33 : 10-02-2016, 11:22:06 »
Самое плохое в том, что на моем компе не удается никак отловить ни в релизной сборке, ни в другой. Вставив 954 блока в одном сеансе, при этом не менее 100 блоков различных вставлял через realdwgfile в цикле. Засомневался даже, что ошибка в коде. Тут задумался, может файл подкачки как-то влияет? Программа стоит на том же диске, что и файл подкачки. И по версии .net хотел уточнить, программа скомпилирована в 3.5. Правильно я понимаю, что версия net с которой работает автокад и соответственно моя сборка можно посмотреть acad.exe.config?

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

  • ADN Club
  • *****
  • Сообщений: 546
  • Карма: 119
Re: Ошибка при использовании WblockCloneObjects
« Ответ #34 : 10-02-2016, 16:04:55 »
Самое плохое в том, что на моем компе не удается никак отловить ни в релизной сборке, ни в другой.
можно добавить Dispose() после использования объектов везде, где можно, кроме редактора и документа.
у тебя даже есть комментированные строки.
Когда транзакция закроется, она вызовет Dispose всех своих объектов.
Но ты можешь сделать это сам, и если повезет ты увидишь ошибку dispose раньше.
но далеко не факт, что ошибка в данной части кода.

Оффлайн Volody1983Автор темы

  • ADN OPEN
  • **
  • Сообщений: 52
  • Карма: 0
Re: Ошибка при использовании WblockCloneObjects
« Ответ #35 : 10-02-2016, 17:18:27 »
Засунул Readdwgfile в событие Load формы. Dispose новой базы засунул в событие Close. Стало гораздо стабильнее. Но стало иногда ловится EWrongDataBase.

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Ошибка при использовании WblockCloneObjects
« Ответ #36 : 10-02-2016, 19:09:10 »
Dispose новой базы засунул в событие Close
Если создание базы была в Using, то никакой Close не нужен. Более того он вреден.
Но стало иногда ловится EWrongDataBase.
Из документации: eWrongDatabase -- if the owners are not from the same database в WblockCloneObjects. Т.е. как раз возможен случай, когда этот метод вызывается для неправильной базы.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN Club
  • *****
  • Сообщений: 546
  • Карма: 119
Re: Ошибка при использовании WblockCloneObjects
« Ответ #37 : 10-02-2016, 20:10:53 »
Засунул Readdwgfile в событие Load формы. Dispose новой базы засунул в событие Close. Стало гораздо стабильнее. Но стало иногда ловится EWrongDataBase.
не стоит не то и не туда засовывать. Сперва, как предложил Александр, создай тест, где будет выполняться загрузка без всяких форм.
Если это не поможет - делай простейшую тестовую команду на основе этого: http://adndevblog.typepad.com/autocad/2012/05/insert-block-from-a-different-dwg-using-net-.html
Явно укажи в ней имя файла и имя блока. Проверь её работоспособность. Не забывай передавать dll-файл в конфигурации Release.

это позволит узнать, проблема в  функции ReadDwgFile, или в чем-то еще.

Оффлайн Volody1983Автор темы

  • ADN OPEN
  • **
  • Сообщений: 52
  • Карма: 0
Re: Ошибка при использовании WblockCloneObjects
« Ответ #38 : 10-02-2016, 20:15:07 »
Александр я имею ввиду так:

Код - vb.net [Выбрать]
  1. Public Class InsertBlkRefFromDataBaseForm
  2.    Private SourceDb As New Database(False, True)
  3.    Private Sub InsertBlkRefFromDataBaseForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
  4.    SourceDb.ReadDwgFile("Путь к файлу", System.IO.FileShare.Read, True, "")
  5.     End Sub
  6.  
  7. Private Sub InsertBlkRefFromDataBaseForm_FormClosed(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles MyBase.FormClosed
  8.            SourceDb.Dispose()
  9.     End Sub
  10.  
  11. End Class
В код еще добавил переключалку текущей базы  HostApplicationServices.WorkingDatabase = база с которой работаем. При таком варианте ощутимо быстрее, т.к. один раз вызывается readdwgfile, а на его вызов тратится существенное время.

Оффлайн Volody1983Автор темы

  • ADN OPEN
  • **
  • Сообщений: 52
  • Карма: 0
Re: Ошибка при использовании WblockCloneObjects
« Ответ #39 : 10-02-2016, 21:54:23 »
Попробовал докопаться до истины. Первое что сделал создал тест файл с 300 блоков на основе окружностей, без атрибутов и не димамические. Код который ниже отрабатывал эти блоки на основе окружности на ура. Дальше решил со своими блоками потестировать (с атрибутами и динамические). Вообщем код ниже на файле во вложении при запуске команды test в релизной сборке стабильно рушит автокад (тестировал в Autocad MEP 2011 х64 под Win 7 64). В дебаггере по крайней мере раз 5 на чистых файлах отработало без ошибки.
Код - vb.net [Выбрать]
  1.      <CommandMethod("Test")> _
  2.       Public Sub RunWblockCloneObjects()
  3.             Dim doc As Document = Application.DocumentManager.MdiActiveDocument
  4.             Dim db As Database = doc.Database
  5.             Dim ed As Editor = doc.Editor
  6.             Dim _List As New List(Of String)
  7.             _List.Add("Клеммники.281-657")
  8.             _List.Add("Клеммники.281-663")
  9.             _List.Add("Клеммники.282-681")
  10.             _List.Add("Клеммники.282-684")
  11.             _List.Add("Клеммники.284-687")
  12.             _List.Add("Клеммники.870-551")
  13.             _List.Add("Клеммники.870-557")
  14.             _List.Add("Клеммники.870-567")
  15.             _List.Add("Сх.281-657")
  16.             _List.Add("Сх.281-663")
  17.             _List.Add("Сх.282-681")
  18.             _List.Add("Сх.282-684")
  19.             _List.Add("Сх.284-681")
  20.             _List.Add("Сх.284-687")
  21.             _List.Add("Сх.870-551")
  22.             _List.Add("Сх.870-557")
  23.             _List.Add("Сх.870-567")
  24.             _List.Add("Сх.Одноярусный_кл_3проводный")
  25.             For Each _Name In _List
  26.  
  27.                 AddListBlockRecordToActiveDatabase("X:\TestBlk.dwg", _Name)
  28.             Next
  29.         End Sub
  30.         'Вытягивает нужные блоки из внешнего файла если их нет в текущем
  31.         Private Sub AddListBlockRecordToActiveDatabase(ByVal PathString As String, ByVal BlkName As String)
  32.             Dim ed As Editor = Application.DocumentManager.MdiActiveDocument.Editor
  33.             Dim doc As Document = Application.DocumentManager.MdiActiveDocument
  34.             Dim ActiveDb As Database = doc.Database
  35.             Using SourceDb As New Database(False, True)
  36.  
  37.                 SourceDb.ReadDwgFile(PathString, System.IO.FileShare.Read, True, "")
  38.                 Dim RequestedBlockIds As New ObjectIdCollection()
  39.                 HostApplicationServices.WorkingDatabase = SourceDb
  40.                 Using acTrans As Transaction = SourceDb.TransactionManager.StartTransaction()
  41.                     Dim bt As BlockTable = DirectCast(acTrans.GetObject(SourceDb.BlockTableId, OpenMode.ForRead), BlockTable)
  42.                     If bt.Has(BlkName) Then
  43.                         Dim btr As BlockTableRecord = DirectCast(acTrans.GetObject(bt(BlkName), OpenMode.ForRead), BlockTableRecord)
  44.                         RequestedBlockIds.Add(btr.ObjectId)
  45.                     End If
  46.                     acTrans.Commit()
  47.                 End Using
  48.                 HostApplicationServices.WorkingDatabase = ActiveDb
  49.                 If RequestedBlockIds.Count > 0 Then
  50.                     Dim mapping As New IdMapping()
  51.                     Using doclock As DocumentLock = doc.LockDocument()
  52.                         ActiveDb.WblockCloneObjects(RequestedBlockIds, ActiveDb.BlockTableId, mapping, DuplicateRecordCloning.Ignore, False)
  53.                     End Using
  54.                 End If
  55.  
  56.             End Using
  57.         End Sub
Перегнал тоже самое в C# конвертером:
Код - C# [Выбрать]
  1. [CommandMethod("Test")]
  2. public void RunWblockCloneObjects()
  3. {
  4.         Document doc = Application.DocumentManager.MdiActiveDocument;
  5.         Database db = doc.Database;
  6.         Editor ed = doc.Editor;
  7.         List<string> _List = new List<string>();
  8.         _List.Add("Клеммники.281-657");
  9.         _List.Add("Клеммники.281-663");
  10.         _List.Add("Клеммники.282-681");
  11.         _List.Add("Клеммники.282-684");
  12.         _List.Add("Клеммники.284-687");
  13.         _List.Add("Клеммники.870-551");
  14.         _List.Add("Клеммники.870-557");
  15.         _List.Add("Клеммники.870-567");
  16.         _List.Add("Сх.281-657");
  17.         _List.Add("Сх.281-663");
  18.         _List.Add("Сх.282-681");
  19.         _List.Add("Сх.282-684");
  20.         _List.Add("Сх.284-681");
  21.         _List.Add("Сх.284-687");
  22.         _List.Add("Сх.870-551");
  23.         _List.Add("Сх.870-557");
  24.         _List.Add("Сх.870-567");
  25.         _List.Add("Сх.Одноярусный_кл_3проводный");
  26.         foreach (object _Name_loopVariable in _List) {
  27.                 _Name = _Name_loopVariable;
  28.  
  29.                 AddListBlockRecordToActiveDatabase("X:\\TestBlk.dwg", _Name);
  30.         }
  31. }
  32. //Вытягивает нужные блоки из внешнего файла если их нет в текущем
  33. private void AddListBlockRecordToActiveDatabase(string PathString, string BlkName)
  34. {
  35.         Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
  36.         Document doc = Application.DocumentManager.MdiActiveDocument;
  37.         Database ActiveDb = doc.Database;
  38.         using (Database SourceDb = new Database(false, true)) {
  39.  
  40.                 SourceDb.ReadDwgFile(PathString, System.IO.FileShare.Read, true, "");
  41.                 ObjectIdCollection RequestedBlockIds = new ObjectIdCollection();
  42.                 HostApplicationServices.WorkingDatabase = SourceDb;
  43.                 using (Transaction acTrans = SourceDb.TransactionManager.StartTransaction()) {
  44.                         BlockTable bt = (BlockTable)acTrans.GetObject(SourceDb.BlockTableId, OpenMode.ForRead);
  45.                         if (bt.Has(BlkName)) {
  46.                                 BlockTableRecord btr = (BlockTableRecord)acTrans.GetObject(bt(BlkName), OpenMode.ForRead);
  47.                                 RequestedBlockIds.Add(btr.ObjectId);
  48.                         }
  49.                         acTrans.Commit();
  50.                 }
  51.                 HostApplicationServices.WorkingDatabase = ActiveDb;
  52.                 if (RequestedBlockIds.Count > 0) {
  53.                         IdMapping mapping = new IdMapping();
  54.                         using (DocumentLock doclock = doc.LockDocument()) {
  55.                                 ActiveDb.WblockCloneObjects(RequestedBlockIds, ActiveDb.BlockTableId, mapping, DuplicateRecordCloning.Ignore, false);
  56.                         }
  57.                 }
  58.  
  59.         }
  60. }


« Последнее редактирование: 10-02-2016, 22:28:22 от Volody1983 »

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Ошибка при использовании WblockCloneObjects
« Ответ #40 : 10-02-2016, 21:57:34 »
При таком варианте ощутимо быстрее, т.к. один раз вызывается readdwgfile, а на его вызов тратится существенное время.
Это правда. Но это если блоки у тебя хранятся в одном файле и ты до закрытия формы занимаешься их вставкой.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Volody1983Автор темы

  • ADN OPEN
  • **
  • Сообщений: 52
  • Карма: 0
Re: Ошибка при использовании WblockCloneObjects
« Ответ #41 : 10-02-2016, 22:12:07 »
Александр Ривилис,
Если у Вас есть возможность проверьте плиз код в 39 посте на файле с блоками при которых рушится автокад (файл в том же посте приложен)?

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Ошибка при использовании WblockCloneObjects
« Ответ #42 : 11-02-2016, 00:54:11 »
Если у Вас есть возможность проверьте плиз код в 39 посте на файле с блоками при которых рушится автокад (файл в том же посте приложен)?
Я проверил твой вариант (VB - Release) в чистом AutoCAD 2012 (2011 версии у меня нет) - всё сработало нормально.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Ошибка при использовании WblockCloneObjects
« Ответ #43 : 11-02-2016, 01:12:48 »
Можешь попробовать еще такой вариант (без транзакции и без открытия блока в исходном файле, что совсем лишнее):
Код - vb.net [Выбрать]
  1. 'Вытягивает нужные блоки из внешнего файла если их нет в текущем
  2. Private Sub AddListBlockRecordToActiveDatabase(ByVal PathString As String, ByVal BlkName As String)
  3.     Dim ed As Editor = Application.DocumentManager.MdiActiveDocument.Editor
  4.     Dim doc As Document = Application.DocumentManager.MdiActiveDocument
  5.     Dim ActiveDb As Database = doc.Database
  6.     Using SourceDb As New Database(False, False)
  7.         SourceDb.ReadDwgFile(PathString, System.IO.FileShare.Read, True, "")
  8.         SourceDb.CloseInput(True)
  9.         Dim RequestedBlockIds As New ObjectIdCollection()
  10.         Using bt As BlockTable =
  11.             DirectCast(SourceDb.BlockTableId.Open(OpenMode.ForRead), BlockTable)
  12.             If bt.Has(BlkName) Then
  13.                 RequestedBlockIds.Add(bt(BlkName))
  14.             Else
  15.                 Return
  16.             End If
  17.         End Using
  18.         If RequestedBlockIds.Count > 0 Then
  19.             Dim mapping As New IdMapping()
  20.             Using doclock As DocumentLock = doc.LockDocument()
  21.                 ActiveDb.WblockCloneObjects(RequestedBlockIds, ActiveDb.BlockTableId,
  22.                                             mapping, DuplicateRecordCloning.Replace, False)
  23.             End Using
  24.         End If
  25.     End Using
  26. End Sub
  27.  
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • ADN Club
  • *****
  • Сообщений: 546
  • Карма: 119
Re: Ошибка при использовании WblockCloneObjects
« Ответ #44 : 11-02-2016, 08:16:21 »
тестировать лучше по шагам. Александр дал уже готовый код, но тогда ошибку возможно не увидим. А хотелось бы.

1. Убери лишние строки в RunWblockCloneObjects:
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
Там нет работы с БД или документом

2. Попробуй убрать строки:
HostApplicationServices.WorkingDatabase = SourceDb;
HostApplicationServices.WorkingDatabase = ActiveDb;
Для цели вставки блоков, переключать не нужно.

3. Замени(Как у Александра)
BlockTableRecord btr = (BlockTableRecord)acTrans.GetObject(bt(BlkName), OpenMode.ForRead);
RequestedBlockIds.Add(btr.ObjectId);
на
RequestedBlockIds.Add(bt(BlkName))

4. Попробуй добавить Dispose(), после того, как закончил использовать:
ObjectIdCollection RequestedBlockIds
IdMapping mapping

5. Возможно крашит частое переключение блокировки листа DocumentLock doclock = doc.LockDocument()
Вынеси его в RunWblockCloneObjects()

6. Попробуй вставить в блок try-Catch:
ActiveDb.WblockCloneObjects(RequestedBlockIds, ActiveDb.BlockTableId, mapping, DuplicateRecordCloning.Ignore, false);
...у меня такое было, когда пытался клонировать прокси
И нужно вывести в строку имя блока, на каком блоке не выполнился WblockCloneObjects

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