Editor.PointMonitor - ошибка при отключении отслеживания в методе отслеживания.

Автор Тема: Editor.PointMonitor - ошибка при отключении отслеживания в методе отслеживания.  (Прочитано 10100 раз)

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

Тема содержит сообщение с Решением. Нажмите здесь чтобы посмотреть его.

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Здравствуйте!
Уже не в первый раз сталкиваюсь с тем, что в не могу отключить отслеживание события Editor.PointMonitor в методе отслеживания этого события! Чем оно такое особенное? Вот пример двух команд:
Код - C# [Выбрать]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. using Autodesk.AutoCAD.Runtime;
  7. using Autodesk.AutoCAD.EditorInput;
  8. using Autodesk.AutoCAD.ApplicationServices;
  9.  
  10. namespace EditorPointMonitor
  11. {
  12.     public class Class1
  13.     {
  14.         [CommandMethod("TestPointMonitor")]
  15.         public void EditorPointMonitorTestRun()
  16.         {
  17.             Document adoc = Application.DocumentManager.MdiActiveDocument;
  18.             if (adoc != null)
  19.             {
  20.                 Editor ed = adoc.Editor;
  21.                 ed.PointMonitor += new PointMonitorEventHandler(ed_PointMonitor);
  22.             }
  23.         }
  24.  
  25.         void ed_PointMonitor(object sender, PointMonitorEventArgs e)
  26.         {
  27.             (sender as Editor).PointMonitor -= new PointMonitorEventHandler(ed_PointMonitor);
  28.         }
  29.  
  30.         [CommandMethod("TestCommandRun")]
  31.         public void CommandRunTest()
  32.         {
  33.             Document adoc = Application.DocumentManager.MdiActiveDocument;
  34.             if (adoc != null)
  35.             {
  36.                 adoc.CommandEnded += new CommandEventHandler(adoc_CommandEnded);
  37.             }
  38.         }
  39.  
  40.         void adoc_CommandEnded(object sender, CommandEventArgs e)
  41.         {
  42.             (sender as Document).CommandEnded -= new CommandEventHandler(adoc_CommandEnded);
  43.         }
  44.     }
  45. }
  46.  
Команда "TestPointMonitor" гарантированно завершит аварийно работу AutoCAD, тогда как "TestCommandRun" не вызывает никаких проблем. В чем может быть причина?

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

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Вопрос конечно интересный. Думаю, что это баг в реализации. Особенность события PointMonitor заключается в том, что вызов этого события происходит много раз в секунду, при этом (видимо для оптимизации) исключена дополнительная проверка значения на значение m_pointMonitor при выходе из обработчика события. А он становится равным 0:

Интересно, что если вызвать ed.PointMonitor += new PointMonitorEventHandler(ed_PointMonitor); в команде TestPointMonitor, то первый раз ed_PointMonitor отработает без ошибок, а второй раз аварийно завершится. При этом в первый раз по выходе из события m_pointMonitor !=0, а во второй раз m_pointMonitor == 0.
Так как достаточно легко обойти этот баг путём введения некой глобальной переменной, я не вижу в нём большой проблемы. Но если хочешь могу отправить в ADN DevHelp.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Да, именно с помощью переменной и выкручиваюсь. У меня AutoCAD, вроде бы, падает сразу при выходе из метода ed_PointMonitor, а не во второй раз... Ну не сильно это важно, думаю. А вот что скажут на это в ADN - мне было бы интересно! Если Вас это не сильно затруднит, конечно :)

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

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
У меня AutoCAD, вроде бы, падает сразу при выходе из метода ed_PointMonitor, а не во второй раз...
Ты видимо не понял. Если изменить твой код TestPointMonitor на такой:

Код - C# [Выбрать]
  1.         [CommandMethod("TestPointMonitor")]
  2.         public void EditorPointMonitorTestRun()
  3.         {
  4.             Document adoc = Application.DocumentManager.MdiActiveDocument;
  5.             if (adoc != null)
  6.             {
  7.                 Editor ed = adoc.Editor;
  8.                 ed.PointMonitor += new PointMonitorEventHandler(ed_PointMonitor);
  9.                 ed.PointMonitor += new PointMonitorEventHandler(ed_PointMonitor);
  10.             }
  11.         }
т.е. на событие подписаться дважды, то в этом случае в первый раз ed_PointMonitor отработает нормально, а во втором при выходе из ed_PointMonitor будет Fatal Error.

А вот что скажут на это в ADN - мне было бы интересно! Если Вас это не сильно затруднит, конечно :)
Договорились.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Отправил запрос в ADN - подозреваю, что они подтвердят баг и пообещают в будущих версиях исправить.
Кстати, проверял вариант со статическим классом и обработчиком события - в других случаях это помогает, но не в этом...
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Отправил запрос в ADN - подозреваю, что они подтвердят баг и пообещают в будущих версиях исправить.
Спасибо! А вдруг скажут, что так и должно быть? :) Хотя вряд ли, конечно...
Я когда первый раз столкнулся с этим, долго искал причину падения AutoCAD - падает без каких-либо сообщений, просто тихо умирает. Пришлось расставлять кучу точек останова, запускать отладку и отслеживать проблемное место.
До этого уже приходилось отключать обработчики внутри метода обработки. Например - при модификации объекта базы чертежа, если нужно выполнить модификацию связанного с ним объекта и не запустить бесконечный цикл, нужно было отключать отслеживание события модификации. И никаких проблем с этим не было.
А вот вчера снова напоролся на это. Когда увидел сообщение о прекращении работы AutoCAD, практически сразу понял в чем дело :) Вы правильно подметили - неприятно, но решаемо. Так что, будут это исправлять или нет - это уже их дело. Главное, что я уже об этой проблеме знаю, Вы им о ней рассказали - и они теперь знают :) (а может и раньше знали). Может быть кто-то еще прочитает и для себя отметит.
Кстати, проверял вариант со статическим классом и обработчиком события - в других случаях это помогает, но не в этом...
Хм, я и не думал, что у статического может быть другое поведение.

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

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Выскажу еще дополнительное предположение. Видимо m_pointMonitor - это что-то типа Dictionary<Document,PointMonitorEventHandler>, который зачем-то удаляют и обнуляют в случае если количество элементов становится равным нулю. Причем, AutoCAD по выходе из обработчика этого события не проверят m_pointMonitor на 0 и использует его.
Код - C# [Выбрать]
  1. // Autodesk.AutoCAD.EditorInput.Editor
  2. public unsafe event PointMonitorEventHandler PointMonitor
  3. {
  4.         add
  5.         {
  6.                 if (this.m_pointMonitor == null)
  7.                 {
  8.                         AcMgPointMonitor* ptr = <Module>.@new(32uL);
  9.                         AcMgPointMonitor* pointMonitor;
  10.                         try
  11.                         {
  12.                                 if (ptr != null)
  13.                                 {
  14.                                         pointMonitor = <Module>.Autodesk.AutoCAD.EditorInput.AcMgPointMonitor.{ctor}(ptr, this);
  15.                                 }
  16.                                 else
  17.                                 {
  18.                                         pointMonitor = 0L;
  19.                                 }
  20.                         }
  21.                         catch
  22.                         {
  23.                                 <Module>.delete((void*)ptr);
  24.                                 throw;
  25.                         }
  26.                         this.m_pointMonitor = pointMonitor;
  27.                 }
  28.                 <Module>.Autodesk.AutoCAD.EditorInput.AcMgPointMonitor.add(this.m_pointMonitor, value);
  29.         }
  30.         remove
  31.         {
  32.                 AcMgPointMonitor* pointMonitor = this.m_pointMonitor;
  33.                 if (pointMonitor != null && <Module>.Autodesk.AutoCAD.EditorInput.AcMgPointMonitor.remove(pointMonitor, value))
  34.                 {
  35.                         AcMgPointMonitor* pointMonitor2 = this.m_pointMonitor;
  36.                         if (pointMonitor2 != null)
  37.                         {
  38.                                 object arg_27_0 = calli(System.Void* modopt(System.Runtime.CompilerServices.CallConvCdecl)(System.IntPtr,System.UInt32), pointMonitor2, 1, *(*(long*)pointMonitor2));
  39.                         }
  40.                         this.m_pointMonitor = null;
  41.                 }
  42.         }
  43. }
  44.  
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Ха. Я уповал на то, что уж в чистом ObjectARX (C++) такого быть не должно. Но не тут то было. Всё тоже самое. Добавил эту информацию к запросу к ADN DevHelp.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Загорулькин Дмитрий, мне подтвердили такое поведение - не всё может работать внутри обработчиков событий.  Очень интересовались в каком случае это может пригодиться - нужно чтобы оформить заявку на исправление.
Есть мысли по этому поводу?
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
В последний раз я столкнулся с этим, когда нужно было отследить расстояние, пройденное курсором. Подписавшись на событие Editor.PointMonitor, при перемещении курсора я суммировал его пройденный путь. Когда он достигал заданной максимальной величины, мне больше не нужно было отслеживать положение курсора и я хотел его тут же в обработчике отключить.
То есть, в основе лежит идея:
0) запускаем обработчик события PointMonitor -> 1) получаем положение курсора -> 2) проверяем выполнение условия -> 3) выполняется?:
- > Нет -> выполняем обработку текущих данных и продолжаем получать данные события 1)
- > Да -> отключаем обработчик PointMonitor и запускаем метод обработки выполнения условия

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

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Это всё происходит вне команды?  Я правильно понял? Т.е. убрать обработчик этого события в любом случае не получится, т.к. ни одно другое событие, в котором его можно было бы убрать, не происходит.
P.S.: Это не задача с висящим на курсоре тултипом?
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Запуск события происходит в команде - шаг 0), а дальше уже отслеживание и команда никак не связаны.
В конкретно моем случае - я делал вывод сообщения около курсора, которое пропадает после определенного "пробега" курсора (http://forum.dwg.ru/showthread.php?t=89918&page=5). Немодальное WPF окошко с сообщением, которое бегает некоторое время за курсором независимо от запустившей ее команды :)

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

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Ага. Понял. Видел твою тему на dwg.ru
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Отмечено как Решение Александр Ривилис 08-10-2014, 21:47:56

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

  • Administrator
  • *****
  • Сообщений: 13829
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Заявка на изменение поведения обработчика события Editor.PointMonitor подана. Так что есть шансы, что в будущих версиях исправят.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн dmitrymaslakov

  • ADN OPEN
  • Сообщений: 40
  • Карма: 1
Команда "TestPointMonitor" гарантированно завершит аварийно работу AutoCAD
У меня AutoCAD 2014. Аварийного завершения не происходит.
При этом в первый раз по выходе из события m_pointMonitor !=0, а во второй раз m_pointMonitor == 0
Я пробовал подписыватся второй раз на событие. m_pointMonitor как был !=0, так и остался !=0.
Так как достаточно легко обойти этот баг путём введения некой глобальной переменной, я не вижу в нём большой проблемы.
Можете пояснить поподробнее? Я не понял, что делает эта переменная.