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

ADN Club => AutoCAD .NET API => Тема начата: Андрей Бушман от 24-07-2014, 14:49:45

Название: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Андрей Бушман от 24-07-2014, 14:49:45
AutoCAD 2015 SP1.

Как известно, начиная с версии R20.0 (AutoCAD 2015) свойство MdiActiveDocument может оказаться равным null, в виду чего следует всегда выполнять соответствующую проверку. Однако оказывается, что это ещё не все "пряники"...

Код:
Код - C# [Выбрать]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Reflection;
  6. using System.Windows.Forms;
  7.  
  8. #if AUTOCAD
  9. using cad = Autodesk.AutoCAD.ApplicationServices.Application;
  10. using Ap = Autodesk.AutoCAD.ApplicationServices;
  11. using Db = Autodesk.AutoCAD.DatabaseServices;
  12. using Ed = Autodesk.AutoCAD.EditorInput;
  13. using Rt = Autodesk.AutoCAD.Runtime;
  14. using Us = Autodesk.AutoCAD.DatabaseServices.SymbolUtilityServices;
  15. using Hs = Autodesk.AutoCAD.DatabaseServices.HostApplicationServices;
  16. #endif
  17.  
  18. [assembly: Rt.ExtensionApplication(typeof(AcadHelloFromBundle.Class1))]
  19. [assembly: Rt.CommandClass(typeof(AcadHelloFromBundle.Class1))]
  20.  
  21. namespace AcadHelloFromBundle {
  22.     public sealed class Class1 : Rt.IExtensionApplication {
  23.  
  24.         void WriteMessage(String methodName) {
  25.             String message = String.Format(
  26.  
  27.                 "\nHello from the \"{0}\" method!\n", methodName);
  28.             MessageBox.Show(message, "Hello", MessageBoxButtons.OK,
  29.                 MessageBoxIcon.Information);
  30.         }
  31.  
  32.         [Rt.CommandMethod("TEST", Rt.CommandFlags.Session)]
  33.         public void Test() {
  34.             Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
  35.             using (doc.LockDocument()) {
  36.                 WriteMessage(MethodBase.GetCurrentMethod().Name);
  37.             }
  38.         }
  39.  
  40.         #region IExtensionApplication Members
  41.  
  42.         public void Initialize() {
  43.             Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
  44.             if (null == doc) {
  45.                 cad.DocumentManager.DocumentActivated += DocActivated;
  46.             }
  47.             else {
  48.                 using (doc.LockDocument()) {
  49.                     WriteMessage(MethodBase.GetCurrentMethod().Name);
  50.                 }
  51.             }
  52.         }
  53.  
  54.         void DocActivated(object sender, Ap.DocumentCollectionEventArgs e) {
  55.             cad.DocumentManager.DocumentActivated -= DocActivated;
  56.             Ap.Document doc = e.Document;
  57.             using (doc.LockDocument()) {
  58.                 WriteMessage(MethodBase.GetCurrentMethod().Name);
  59.             }
  60.         }
  61.  
  62.         public void Terminate() {
  63.             // throw new NotImplementedException();
  64.         }
  65.         #endregion
  66.     }
  67. }

Сборка зарегистрирована в реестре (ветка HKCU). При старте AutoCAD 2014, код успешно загружается. Первым делом появляется приветствие:

(https://adn-cis.org/forum/proxy.php?request=http%3A%2F%2Fstorage8.static.itmages.ru%2Fi%2F14%2F0724%2Fh_1406195938_8573467_1e2b5116ba.png&hash=d0dc10bcdbc43d0d2b8e863c945b63ae)

При этом пока не появились ни вкладки документов, ни риббоны. Обращаю внимание на то, что в методе Initialize объект MdiActiveDocument уже не равен null. Так же обратите внимание на то, что на заднем фоне видим фон уже открытой Model (хотя сама вкладка ещё не появилась). И лишь после нажатия ОК, появляются все недостающие причиндалы:

(https://adn-cis.org/forum/proxy.php?request=http%3A%2F%2Fstorage5.static.itmages.ru%2Fi%2F14%2F0724%2Fh_1406196466_8960254_bb13736956.png&hash=e9ee66942d8b59d984f2064e25713bee)

То, что риббоны и вкладки документов появляются позднее - это (имхо) не смертельно и обозначается лишь как дополнительная информация. Вопрос, касающийся данной ситуации будет обозначен ниже.

Однако более меня всё же интересует иной момент: меняем код метода WriteMessage следующим образом:

Код - C# [Выбрать]
  1. void WriteMessage(String methodName) {
  2.     String message = String.Format(
  3.  
  4.         "\nHello from the \"{0}\" method!\n", methodName);
  5.  
  6.     //MessageBox.Show(message, "Hello", MessageBoxButtons.OK,
  7.     //    MessageBoxIcon.Information);
  8.  
  9.     Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
  10.     if (null == doc || doc.IsDisposed)
  11.         throw new Exception("Invalid MdiActiveDocument.");
  12.  
  13.     doc.Editor.WriteMessage(message);
  14. }

В данном случае при старте AutoCAD 2015 уже не будет сразу открываться документ, как в предыдущем случае. Вместо этого видим следующее:

(https://adn-cis.org/forum/proxy.php?request=http%3A%2F%2Fstorage6.static.itmages.ru%2Fi%2F14%2F0724%2Fh_1406196977_2354924_ab4b9b98f0.png&hash=af42ba0f534978136991c394dfa8327e)

Однако отладчик показывает, что MdiActiveDocument и в этом случае не null, сообщение успешно отправляется в консоль. Вот только в какую? Отладчик показывает, что объект, на который указывает MdiActiveDocument имеет имя Drawing1.dwg. Но чертежи ведь не открыты, как видим на скрине... Если теперь создать новый чертёж, то он  так же автоматом получает имя Drawing1.dwg.

Хорошо, вполне возможно, что первый чертёж Drawing1.dwg временно создаётся и затем убивается к тому времени, как будет отображено то, что я показал на скрине. После его удаления имя может быть использовано повторно (что AutoCAD 2015 и делает). Проверяю предположение:

Код - C# [Выбрать]
  1. public void Initialize() {
  2.     cad.DocumentManager.DocumentDestroyed += DocDestroyed; // Добавил это
  3.     Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
  4.     if (null == doc) {
  5.         cad.DocumentManager.DocumentActivated += DocActivated;
  6.     }
  7.     else {
  8.         using (doc.LockDocument()) {
  9.             WriteMessage(MethodBase.GetCurrentMethod().Name);
  10.         }
  11.     }            
  12. }
  13.  
  14. void DocDestroyed(object sender, Ap.DocumentDestroyedEventArgs e) {
  15.     // На вызов этого метода ставим брэйкпоинт.
  16. }

Запускаю код и вижу, что брэйкпоинт в методе DocDestroyed действительно срабатывает до того, как появляется предыдущий мой скрин - т.е. моё предположение оказалось верным. Однако нельзя сказать, что это меня радует...

Зачастую, при загрузке моих сборок я вывожу в консоль текущего документа некоторый текст, сигнализирующий о том, что загрузка прошла успешно. Там же может находится некоторая подсказка для пользователя: например перечень доступных команд. Однако в данном случае получается, что при автозагрузке мой текст может отправиться вовсе не туда, куда нужно и юзер не увидит обозначенную информацию. Это достаточно неудобно.

Как вариант, можно подключиться на событие DocumentCreated и в обработчике выводить информацию в консоль создаваемого документа. Однако в том случае, если сборка загружена не при помощи автозагрузчика AutoCAD, но вручную, при помощи NETLOAD, юзер снова не увидит это сообщение (в текущем документе), но вместо этого он увидит его лишь в очередном созданном документе, что опять не есть хорошо.

Далее собственно вопросы:

1. Интересует приемлемое решение обозначенной проблемы.
2. Почему при открытии модального окна наблюдается иное поведение (первый вариант кода)?
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Александр Ривилис от 24-07-2014, 15:56:06
1. Интересует приемлемое решение обозначенной проблемы.
Вопрос передам в ADN DevHelp, хотя я думаю что ты сам догадался, что видимо начиная с AutoCAD 2015 в консоль можно будет выводить не всегда. Впрочем в ObjectARX такое уже было. Кстати, кое что может зависеть и от состояния системной переменной STARTUP.
2. Почему при открытии модального окна наблюдается иное поведение (первый вариант кода)?
А что тебе в этом удивляет? Модальное окно никак не привязано к документу и соответственно к консоли AutoCAD
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Андрей Бушман от 24-07-2014, 17:00:19
А что тебе в этом удивляет? Модальное окно никак не привязано к документу и соответственно к консоли AutoCAD
Меня не то, чтобы удивляет, но несколько напрягает тот факт, что содержимое расширения может непреднамеренно повлиять на состояние AutoCAD при его инициализации. В одном случае на конечном этапе мы получаем уже открытый документ, а во втором - нет. Считаю такое поведение некорректным (имхо).
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Александр Ривилис от 24-07-2014, 17:06:20
В одном случае на конечном этапе мы получаем уже открытый документ, а во втором - нет.
Ах вот ты о чем. Я видимо не понял сразу что тебя насторожило. Нужно подумать.
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Александр Ривилис от 01-08-2014, 00:17:20
Как вариант, можно подключиться на событие DocumentCreated и в обработчике выводить информацию в консоль создаваемого документа. Однако в том случае, если сборка загружена не при помощи автозагрузчика AutoCAD, но вручную, при помощи NETLOAD, юзер снова не увидит это сообщение (в текущем документе), но вместо этого он увидит его лишь в очередном созданном документе, что опять не есть хорошо.
А если подписаться на DocumentLockModeWillChange? Ну само собой сразу отписаться после первого вывода сообщения.
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Андрей Бушман от 01-08-2014, 10:59:24
А если подписаться на DocumentLockModeWillChange? Ну само собой сразу отписаться после первого вывода сообщения.
А что толку? Ведь будет это происходить всё в том же "призраке" Drawing1.dwg, который временно создаётся AutoCAD'ом при старте и затем удаляется (обозначен выше в теме)... Сообщение по прежнему будет попадать именно в его консоль и юзер ничего не увидит.

Код - C# [Выбрать]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Reflection;
  6. using System.Windows.Forms;
  7.  
  8. #if AUTOCAD
  9. using cad = Autodesk.AutoCAD.ApplicationServices.Application;
  10. using Ap = Autodesk.AutoCAD.ApplicationServices;
  11. using Db = Autodesk.AutoCAD.DatabaseServices;
  12. using Ed = Autodesk.AutoCAD.EditorInput;
  13. using Rt = Autodesk.AutoCAD.Runtime;
  14. using Us = Autodesk.AutoCAD.DatabaseServices.SymbolUtilityServices;
  15. using Hs = Autodesk.AutoCAD.DatabaseServices.HostApplicationServices;
  16. #endif
  17.  
  18. [assembly: Rt.ExtensionApplication(typeof(AcadHelloFromBundle.Class1))]
  19. [assembly: Rt.CommandClass(typeof(AcadHelloFromBundle.Class1))]
  20.  
  21. namespace AcadHelloFromBundle {
  22.     public sealed class Class1 : Rt.IExtensionApplication {
  23.  
  24.         void WriteMessage(String methodName) {
  25.             String message = String.Format("\nHello from the \"{0}\" method!\n",
  26.                 methodName);
  27.  
  28.             //MessageBox.Show(message, "Hello", MessageBoxButtons.OK,
  29.             //    MessageBoxIcon.Information);
  30.  
  31.             Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
  32.             if (null == doc || doc.IsDisposed)
  33.                 throw new Exception("Invalid MdiActiveDocument.");
  34.  
  35.             doc.Editor.WriteMessage(message);
  36.         }
  37.  
  38.         [Rt.CommandMethod("TEST", Rt.CommandFlags.Session)]
  39.         public void Test() {
  40.             Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
  41.             using (doc.LockDocument()) {
  42.                 WriteMessage(MethodBase.GetCurrentMethod().Name);
  43.             }
  44.         }
  45.  
  46.         #region IExtensionApplication Members
  47.  
  48.         public void Initialize() {
  49.             cad.DocumentManager.DocumentLockModeChanged += DocLockModeChanged;
  50.         }
  51.  
  52.         void DocLockModeChanged(object sender,
  53.             Ap.DocumentLockModeChangedEventArgs e) {
  54.             cad.DocumentManager.DocumentLockModeChanged -=
  55.                 DocLockModeChanged;            
  56.             WriteMessage(MethodBase.GetCurrentMethod().Name);
  57.         }
  58.         public void Terminate() { }
  59.         #endregion
  60.     }
  61. }
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Дмитрий Загорулькин от 07-08-2014, 11:50:12
А что толку? Ведь будет это происходить всё в том же "призраке" Drawing1.dwg, который временно создаётся AutoCAD'ом при старте и затем удаляется (обозначен выше в теме)... Сообщение по прежнему будет попадать именно в его консоль и юзер ничего не увидит.
А такой вариант не устроит?:
1. При загрузке плагина в Автокад пробежаться по всем открытым документам и выдать сообщения в них.
2. Подписаться на открытие новых документов и также выдавать в них сообщение.

Я по такому шаблону делаю свои автозагружаемые плагины:
Код - C# [Выбрать]
  1. using System;
  2.  
  3. using Autodesk.AutoCAD.ApplicationServices;
  4. using Autodesk.AutoCAD.DatabaseServices;
  5. using Autodesk.AutoCAD.Runtime;
  6.  
  7. [assembly: ExtensionApplication(typeof(MyNamespace.AssemblyLoad))]
  8.  
  9. namespace MyNamespace
  10. {
  11.     public class AssemblyLoad : IExtensionApplication
  12.     {
  13.         public void Initialize()
  14.         {
  15.             // Проходим по коллекции запущенных документов, применяем к ним обработчик
  16.             foreach (Document adoc in Application.DocumentManager) DocExecute(adoc);
  17.  
  18.             // Добавляем реактор на создание новых документов, чтобы в них тоже включался обработчик
  19.             Application.DocumentManager.DocumentCreated
  20.                 += new DocumentCollectionEventHandler(DocumentManager_DocumentCreated);
  21.  
  22.             Application.DocumentManager.DocumentToBeDestroyed
  23.                 += new DocumentCollectionEventHandler(DocumentManager_DocumentToBeDestroyed);            
  24.         }
  25.  
  26.         public void Terminate()
  27.         {            
  28.         }
  29.  
  30.         /// <summary>
  31.         /// Метод отклика на создание нового документа для добавления обработчика к документу
  32.         /// </summary>
  33.         /// <param name="sender"></param>
  34.         /// <param name="e"></param>
  35.         void DocumentManager_DocumentCreated(object sender, DocumentCollectionEventArgs e)
  36.         {
  37.             DocExecute(e.Document);
  38.         }
  39.  
  40.         /// <summary>
  41.         /// Метод отклика на закрытие документа
  42.         /// </summary>
  43.         /// <param name="sender"></param>
  44.         /// <param name="e"></param>
  45.         void DocumentManager_DocumentToBeDestroyed(object sender, DocumentCollectionEventArgs e)
  46.         {
  47.             DocStopExecute(e.Document);
  48.         }
  49.        
  50.         /// <summary>
  51.         /// Метод, который должен быть выполнен в каждом документе сессии
  52.         /// </summary>
  53.         /// <param name="adoc">Ссылка на документ</param>
  54.         void DocExecute(Document adoc)
  55.         {
  56.             // Как вариант - выводим сообщение
  57.             adoc.Editor.WriteMessage("Hello!");
  58.         }
  59.  
  60.         /// <summary>
  61.         /// Метод, выполняемый при закрытии документа
  62.         /// </summary>
  63.         /// <param name="adoc">Ссылка на документ AutoCAD</param>
  64.         void DocStopExecute(Document adoc)
  65.         {
  66.             // Если нужно выполнить что-то при закрытии документа
  67.         }
  68.     }
  69. }
  70.  
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Андрей Бушман от 07-08-2014, 12:13:01
А такой вариант не устроит?:
Не устроит. Расширение загружается один раз. Соответственно и сообщение о загрузке должно выводиться в консоль один раз. Выводя сообщение о загрузке в консоль каждого документа, ты тем самым создаёшь у пользователя иллюзию многократной загрузки расширения - это неприемлемо.
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Дмитрий Загорулькин от 07-08-2014, 12:22:06
Оно и будет один раз в консоли для каждого документа. Расширение же и для него загружено тоже - оно не в документ же грузится.
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Андрей Бушман от 07-08-2014, 12:24:58
будет один раз в консоли для каждого документа.
Но правильней - в консоль только текущего.
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Дима_ от 07-08-2014, 12:30:27
Рисовать на 5 секунд свое окно без обрамления и прочего и плавно его гасить...
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Андрей Бушман от 07-08-2014, 12:34:14
Рисовать на 5 секунд свое окно без обрамления и прочего и плавно его гасить...
Можно, конечно, рюшечки придумывать, но всё же хочу так, как обычно делаю: именно в консоль текущего документа (и только его). В общем, я так понимаю, это очередное проявление одного из множества багов, которые в каждой новой версии AutoCAD множатся как черви. Титаник медленно идёт ко дну.
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Дима_ от 07-08-2014, 13:04:22
Я так понимаю автодеск решил потихоньку "отползать" от обязательства консоли, но до конца еще не придумал как.
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Дмитрий Загорулькин от 07-08-2014, 13:07:16
Но правильней - в консоль только текущего.
Почему? Предположим, там выводится:
"Приложение супер-пупер загружено. Запускать командой ЗАПУСТИСУПЕРПУПЕРПРИЛОЖЕНИЕ"
Чтобы увидеть эту информацию, пользователю нужно переключаться на первоначальный документ? А если он уже закрыт?
Опять же, не каждый пользователь знает, загружено у него приложение в документ или нет. Лиспы, например, грузятся только в текущий документ. А сообщение дает понять, что именно это в новый документ дополнительно грузить не надо.
Если же надо вывести сообщение одно на все приложение - поддержу Дима_, логичнее вывести "приветственное окошко".
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Андрей Бушман от 07-08-2014, 13:10:06
Почему?
Прочитай внимательней Ответ #2.
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Дмитрий Загорулькин от 07-08-2014, 13:18:53
На мой вопрос там точно ответа нет.
ОК, сформулирую его полностью: Почему правильней выводить информацию только в консоль текущего документа?
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Андрей Бушман от 07-08-2014, 13:40:46
На мой вопрос там точно ответа нет.
Там обозначена основная проблема, поднимаемая в топике.
ОК, сформулирую его полностью: Почему правильней выводить информацию только в консоль текущего документа?
Потому что, как правило, выполняемая операция выводит текстовую информацию в консоль именно текущего документа. Ты ведь когда запускаешь команду audit, не ожидаешь, что она будет тебе выводить отчёт в консоль каждого открытого документа? Аналогично, загрузив своё приложение при помощи NETLOAD - ты запускаешь эту команду из консоли конкретного документа.

Цитировать
Опять же, не каждый пользователь знает, загружено у него приложение в документ или нет. Лиспы, например, грузятся только в текущий документ. А сообщение дает понять, что именно это в новый документ дополнительно грузить не надо.
Юзеру никто не мешает запустить команду, определённую в составе подгруженного расширения, дабы узнать, загружено оно или нет.

Цитировать
Если же надо вывести сообщение одно на все приложение - поддержу Дима_, логичнее вывести "приветственное окошко".
Я предпочитаю писать в консоль. К различного рода всплывающим и|или плавно исчезающим "фенечкам" отношусь негативно (всё должно происходить быстро и ненавязчиво). Если юзер загрузив расширение захочет убедиться в том, что загрузка прошла успешно - он просто тут же посмотрит в консоль. Выводить дублирующуюся информацию во все без исключения документы я не вижу никакого смысла и считаю этот подход плохим. То, что .NET расширения не нужно грузить в каждый документ - юзеры узнают и запоминают достаточно быстро, равно как и то, что LISP приходится грузить в каждый чертёж.
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Дмитрий Загорулькин от 07-08-2014, 13:57:09
Я говорю об информации о загрузке приложения, а отчет команды AUDIT - это результат выполнения команды.
В общем, идею изложил и объяснил как мог, не нравится - дело хозяйское  8)
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Дима_ от 08-08-2014, 14:52:47
У меня нет 2015, но на уровне предположения - а если проверять Document.IsActive, Document.Window.Visible?
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Александр Ривилис от 08-08-2014, 16:13:31
Document.Window.Visible?
Я бы эту идею развил до проверки видима ли командная строка на экране.
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Дима_ от 08-08-2014, 16:17:45
Типо "сграбить"  экран до и после вывода в ком. строку и сравнить? Но так она может у юзера и отключена (или закрыта другим окном) быть (а заглядывает он в нее только по необходимости), да и вобще кривовато как-то. Или есть более "айпишные" варианты?
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Александр Ривилис от 08-08-2014, 16:27:12
Или есть более "айпишные" варианты?
Угу. Через P/Invoke acedGetAcadTextCmdLine и acedGetAcadDockCmdLine.
Только они возвращают указатель на CWnd, эквивалента которого в .NET нет. Пока простые идеи закончились.
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Дима_ от 08-08-2014, 16:45:16
Только они возвращают указатель на CWnd, эквивалента которого в .NET нет.
Но как минимум на null его проверить можно.
Название: Re: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.
Отправлено: Александр Ривилис от 08-08-2014, 16:46:58
Но как минимум на null его проверить можно.
Можно, но это не поможет. Думаю, что это значение никогда не null.