Сообщество программистов Autodesk в СНГ

ADN Club => AutoCAD .NET API => Тема начата: Luca от 03-02-2020, 12:29:20

Название: Как поймать ошибку?
Отправлено: Luca от 03-02-2020, 12:29:20
Здравствуйте.
"Падает" приложение, притом Violation Access, те я даже StackTrace посмотреть не могу...
Как в такой ситуации найти ошибку...
Пробовал отлаживать (точки останова), вроде норм всё, до конца доходит приложение и на выходе падает.
Не соображу в чём дело.
Название: Re: Как поймать ошибку?
Отправлено: Дмитрий Загорулькин от 03-02-2020, 12:37:55
Общие рекомендации:
Смотрите окно "Output" ("Вывод") в Visual Studio при отладке. Есть шанс, что там будет полезная информация.
В настройках отладки добавить тип исключения "AccessViolationException" в список исключений, при которых останавливается отладка. Или ставить точку останова в начале команды и выполнять пошагово до тех пор, пока не упадёт. Это, как минимум, поможет понять в каком месте возникает исключение.
Название: Re: Как поймать ошибку?
Отправлено: Александр Ривилис от 03-02-2020, 12:58:44
Luca,
На этом форуме не гадают на кофейной гуще - нужен исходный код.
Название: Re: Как поймать ошибку?
Отправлено: trir от 03-02-2020, 13:12:51
обычно это обращение к null-объекту
Название: Re: Как поймать ошибку?
Отправлено: Александр Ривилис от 03-02-2020, 13:15:25
обычно это обращение к null-объекту
Или к объекту, который открыт в транзакции, за пределами этой транзакции... Тут вариантов множество...
Название: Re: Как поймать ошибку?
Отправлено: Luca от 03-02-2020, 13:17:49
Автокад падает при команде  AUTO_ELECTRO_NUMBER_BLOCKS_
Решение разделено на части:
Общие вещи собраны в пространстве Common
Название: Re: Как поймать ошибку?
Отправлено: Luca от 03-02-2020, 13:18:57
обычно это обращение к null-объекту
из документации следует, что это грубая ошибка, сродни NullException, но только обращение за границы, а не к Null
Название: Re: Как поймать ошибку?
Отправлено: trir от 03-02-2020, 13:22:52
нельзя так делать
Код - C# [Выбрать]
  1.             public static List<BlockReference> getListOfBlocks(SelectionSet selectionSet, string layerName)
  2.             {
  3.                 List<BlockReference> listOfBlocks = new List<BlockReference>();
  4.                 Transaction transaction = startTransaction();
  5.                 using (transaction)
  6.                 {
  7.                     foreach (SelectedObject selectionSetObject in selectionSet)
  8.                     {
  9.                         if (selectionSetObject != null)
  10.                         {
  11.                             Entity entity = transaction.GetObject(selectionSetObject.ObjectId, OpenMode.ForRead) as Entity;
  12.                             if (entity is BlockReference block)
  13.                             {
  14.                                 if (block.Layer == layerName)
  15.                                 {
  16.                                     listOfBlocks.Add(block);
  17.                                 }
  18.                             }
  19.                         }
  20.                     }
  21.                     transaction.Commit();
  22.                 }
  23.                 if (listOfBlocks.Count != 0)
  24.                 {
  25.                     return listOfBlocks;
  26.                 }
  27.                 else
  28.                 {
  29.                     Application.ShowAlertDialog("[listOfBlocks:] НЕТ БЛОКОВ. КОЛИЧЕСТВО БЛОКОВ = " + listOfBlocks.Count);
  30.                     return null;
  31.                 }
  32.             }

Посмотри как у меня реализовано - функции CrTbl и GetSelect https://github.com/triroakenshield/tblPrs/blob/master/TableParser/MyTable.vb
Название: Re: Как поймать ошибку?
Отправлено: Александр Ривилис от 03-02-2020, 13:28:31
нельзя так делать
Именно то, о чем я и писал выше.
Название: Re: Как поймать ошибку?
Отправлено: Luca от 03-02-2020, 13:30:20
обычно это обращение к null-объекту
Или к объекту, который открыт в транзакции, за пределами этой транзакции... Тут вариантов множество...
Так и есть!
У меня было ощущения, судя по отладке. что с транзакцией что-то не то, но понять что именно я не мог.
Как только Вы сказали, то я сразу нашел transaction.Commit, который не в тех операторных скобках.
Ох, спасибо...
Название: Re: Как поймать ошибку?
Отправлено: Luca от 03-02-2020, 13:30:45
нельзя так делать
Именно то, о чем я и писал выше.
да, я уже понял ... нашел. Спасибо.
Название: Re: Как поймать ошибку?
Отправлено: trir от 03-02-2020, 13:32:10
Цитировать
то я сразу нашел transaction.Commit, который не в тех операторных скобках.
он то как раз там где надо
Название: Re: Как поймать ошибку?
Отправлено: Александр Ривилис от 03-02-2020, 13:35:03
Как только Вы сказали, то я сразу нашел transaction.Commit, который не в тех операторных скобках.
У тебя в Common.cs  полный бред, который даже сложно комментировать.
Например,
Код - C# [Выбрать]
  1.             public static BlockTableRecord getBlockByNameFromDocument(string blockName)
  2.             {
  3.                 Transaction transaction = startTransaction();
  4.  
  5.                 BlockTable blockTable = (BlockTable)transaction.GetObject(database.BlockTableId, OpenMode.ForRead);
  6.                 foreach (ObjectId btrID in blockTable)
  7.                 {
  8.                     BlockTableRecord btr = (BlockTableRecord)transaction.GetObject(btrID, OpenMode.ForWrite);
  9.                     if (btr.Name.ToUpper() == blockName.ToUpper())
  10.                     {
  11.                         return btr;
  12.                     }
  13.                 }
  14.                 return null;
  15.             }
1. Запускается транзакция, но нет ни transaction.Dispose() ни transaction.Commit()
2. Из метода возвращается BlockTableRecord, открытый в транзакции.

Т.е. нужно переделывать всё логику методов.
Название: Re: Как поймать ошибку?
Отправлено: Luca от 03-02-2020, 13:44:30
Как только Вы сказали, то я сразу нашел transaction.Commit, который не в тех операторных скобках.
У тебя в Common.cs  полный бред, который даже сложно комментировать.
Например,
Код - C# [Выбрать]
  1.             public static BlockTableRecord getBlockByNameFromDocument(string blockName)
  2.             {
  3.                 Transaction transaction = startTransaction();
  4.  
  5.                 BlockTable blockTable = (BlockTable)transaction.GetObject(database.BlockTableId, OpenMode.ForRead);
  6.                 foreach (ObjectId btrID in blockTable)
  7.                 {
  8.                     BlockTableRecord btr = (BlockTableRecord)transaction.GetObject(btrID, OpenMode.ForWrite);
  9.                     if (btr.Name.ToUpper() == blockName.ToUpper())
  10.                     {
  11.                         return btr;
  12.                     }
  13.                 }
  14.                 return null;
  15.             }
1. Запускается транзакция, но нет ни transaction.Dispose() ни transaction.Commit()
2. Из метода возвращается BlockTableRecord, открытый в транзакции.

Т.е. нужно переделывать всё логику методов.
метод  getBlockByNameFromDocument пока просто лежит, на будущее, я его не использую.
прям всё и переделывать?
в Common общие для методы и констаны для всех операций, типа получение транзакции, получение предварительного выбора и тд
теперь в следующей команде  AUTO_ELECTRO_CREATE_CABLE_LOG_  тоже Violation Access
Название: Re: Как поймать ошибку?
Отправлено: Александр Ривилис от 03-02-2020, 13:48:58
теперь в следующей команде  AUTO_ELECTRO_CREATE_CABLE_LOG_  тоже Violation Access
Никогда не храни открытые объекты/примитивы - храни их ObjectId. И открывай по мере надобности.
Название: Re: Как поймать ошибку?
Отправлено: Luca от 03-02-2020, 13:50:16
У тебя в Common.cs  полный бред, который даже сложно комментировать.
обсуждалось здесь
https://adn-cis.org/forum/index.php?topic=9705.0 (https://adn-cis.org/forum/index.php?topic=9705.0)
нужно переделывать всё логику методов.
зачем всю логику переделывать всех методов?
Название: Re: Как поймать ошибку?
Отправлено: trir от 03-02-2020, 13:53:13
Цитировать
обсуждалось здесь
ага
Цитировать
для получения ObjectId того BlockTableRecord
Название: Re: Как поймать ошибку?
Отправлено: Александр Ривилис от 03-02-2020, 13:55:12
зачем всю логику переделывать всех методов?
Я не знаю как мне еще нужно объяснять про открытые примитивы/объекты вне транзакций, а у тебя это на каждом шагу. Не должно быть глобальных List<Polyline>, List<BlockReference> и т.д.
Название: Re: Как поймать ошибку?
Отправлено: Luca от 03-02-2020, 13:56:27
Никогда не храни открытые объекты/примитивы - храни их ObjectId. И открывай по мере надобности.
не совсем понял о чем речь
Название: Re: Как поймать ошибку?
Отправлено: Александр Ривилис от 03-02-2020, 13:58:29
не совсем понял о чем речь
Ты хранишь между запусками команд вот такое: List<BlockReference>, List<Polyline> - этого делать категорически нельзя. Вместо этого храни List<ObjectId> для вставок блоков и полилиний и если нужны их данные - открывай их по мере надобности.
Название: Re: Как поймать ошибку?
Отправлено: trir от 03-02-2020, 14:00:28
Цитировать
этого делать категорически нельзя
а я делаю  ::)
Код - C# [Выбрать]
  1.         public void DrawLayer(Data.DataLayer wl)
  2.         {
  3.             //List<Entity> wEntlist = new List<Entity>();
  4.             Autodesk.Gis.Map.ObjectData.Table wTbl = null;
  5.             if (wl.HasAttributes()) wTbl = CreateODTable(wl._Rule);
  6.             ObjectId wid;
  7.             Entity wEnt;
  8.             Geometry.TGeometry g;
  9.             foreach (Data.MyRecord wr in wl.Table)
  10.             {
  11.                 g = wr.GetGeometry();
  12.                 if (g != null)
  13.                 {
  14.                     wEnt = MakeGeometry(g);
  15.                     wid = DrawEntity(wEnt);
  16.                     if (wl.HasAttributes()) AddAttr(wid, wr, wTbl);
  17.                 }
  18.                 //wEntlist.Add(wEnt);
  19.             }
  20.             //DrawEntity(wEntlist);
  21.         }
https://github.com/triroakenshield/RosReestrImpLib/blob/master/RosReestrImp/MyCommands.cs
Название: Re: Как поймать ошибку?
Отправлено: Александр Ривилис от 03-02-2020, 14:04:33
а я делаю 
wEnt у тебя содержится в Database (т.е. ObjectId у него не null) или только в памяти?
Название: Re: Как поймать ошибку?
Отправлено: trir от 03-02-2020, 14:06:34
Только в памяти  ;D, а DrawEntity добавляет его в Database и возвращает ObjectId - с которым дальше и работаю!
Название: Re: Как поймать ошибку?
Отправлено: Александр Ривилис от 03-02-2020, 14:08:40
Только в памяти  ;D, а DrawEntity добавляет его в Database и возвращает ObjectId - с которым дальше и работаю!
Ну так совсем другое дело...
Название: Re: Как поймать ошибку?
Отправлено: Luca от 03-02-2020, 14:11:56
Вместо этого храни List<ObjectId> для вставок блоков и полилиний и если нужны их данные - открывай их по мере надобности.
c этого момента подробнее, пожалуйста... если можно, конечно
что значит открываешь? просто работа очень много и читать исходники, пока, времени просто нет...
не совсем понимаю пока в каких абстрактных типах данных (АТД) всё это хранится, поэтому так и поступаю.
по-идее, как я это понимаю, BlockReference - ссылка ( разыменованный указатель), те указатель, но при обращении к нему получаем АТД. ObjectID, исходя из названия, это какой-то ключ для получения Object из Dictonary, например или какой-то древовидной структуры.
Можно пример, как имея BlockReference получить ObjectID и обратно. ObjectId это ключ для получения BlockReference из какой-то структуры, АТД - что это за структура?
Честно сказать, у меня мозги "заплелись" немного ))
Название: Re: Как поймать ошибку?
Отправлено: trir от 03-02-2020, 14:14:50
Код - vb.net [Выбрать]
  1.     Public Shared Function CrTbl(acDoc As MyAcAs.Document) As MyTable
  2.         'Создаём таблицу
  3.         Dim ed As Editor = acDoc.Editor
  4.         Dim objIdArray() As ObjectId = MyTable.GetSelect(ed) 'Получаем от пользователя набор данных для парсинга
  5.         If objIdArray IsNot Nothing Then
  6.             Dim dbObj As DBObject
  7.             Dim wList As New List(Of Line)
  8.             Dim wTList As New List(Of DBText)
  9.             Dim wMTList As New List(Of MText)
  10.             Using tr As Transaction = acDoc.Database.TransactionManager.StartTransaction
  11.                 Try
  12.                     For Each objId As ObjectId In objIdArray
  13.                         dbObj = tr.GetObject(objId, OpenMode.ForRead)
  14.                         'Сортируем полученные объекты
  15.                         Select Case True
  16.                             Case TypeOf dbObj Is Line
  17.                                 wList.Add(dbObj)
  18.                             Case TypeOf dbObj Is Polyline
  19.                                 wList.AddRange(MyTable.PolyToLine(dbObj))
  20.                             Case TypeOf dbObj Is DBText
  21.                                 wTList.Add(dbObj)
  22.                             Case TypeOf dbObj Is MText
  23.                                 wMTList.Add(dbObj)
  24.                         End Select
  25.                     Next
  26.                     tr.Commit()
  27.                 Catch ex As Exception
  28.                     ed.WriteMessage(ex.ToString())
  29.                     tr.Abort()
  30.                 End Try
  31.             End Using
  32.             '
  33.             Dim wMTbl As MyTable = MyTable.CrTbl(wList)
  34.             'Заполняем текстом
  35.             For Each wt In wTList
  36.                 wMTbl.SetValue(wt)
  37.             Next
  38.             For Each wmt In wMTList
  39.                 wMTbl.SetValue(wmt)
  40.             Next
  41.             Return wMTbl
  42.         Else
  43.             Return Nothing
  44.         End If
  45.     End Function
блин, неправильный пример... но ведь работает
Название: Re: Как поймать ошибку?
Отправлено: Александр Ривилис от 03-02-2020, 14:24:20
Честно сказать, у меня мозги "заплелись" немного ))
Извини, но это единственно что я вынес из твоего сообщения. Не лезь в абстракции. Делай то, что я тебе подсказываю, иначе ты потратишь уйму времени на написание кода, который будет работать через раз и ты будешь создавать постоянно темы, аналогичные этой.
Открытие объекта это или tr.GetObject(id,...) или id.Open(...) - в результате из ObjectId получаем одного из наследников DBObject (например, Entity или BlockTableRecord). Закрытие объекта, открытого через id.Open(...) - это метод Close, а для открытого в транзакции - это или tr.Commit() или tr.Dispose()
Если объект открыт (например, BlockReference) в одной из твоих команд и не закрыт в ней (хранится в static переменной), то это значит, что ни сам AutoCAD, ни другие приложения к нему обратиться уже не смогут.
Название: Re: Как поймать ошибку?
Отправлено: Александр Ривилис от 03-02-2020, 14:26:51
блин, неправильный пример... но ведь работает
Для объектов, открытых ForRead такое иногда работает. Но зачем наступать на грабли???
Название: Re: Как поймать ошибку?
Отправлено: Lemieux от 09-06-2020, 15:50:05
Прошу прощения за некропостинг, но может кто-то объяснит почему автор везде пихает static?
Название: Re: Как поймать ошибку?
Отправлено: Александр Ривилис от 09-06-2020, 15:56:36
Прошу прощения за некропостинг, но может кто-то объяснит почему автор везде пихает static?
1. Это не некропостинг - этой теме всего-то четыре месяца.
2. static бывает достаточно удобен для утилитных функций, которые можно вызвать без создания самого объекта класса.
Но почему автор решил использовать static наверное у автора и нужно спрашивать. Впрочем подозреваю, что с его стороны это просто "копипастинг" готового метода, найденного на просторах интернет.