AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.

Автор Тема: AutoCAD 2015 SP1: текст в консоль вывода документа при загрузке сборки.  (Прочитано 15918 раз)

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

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
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, код успешно загружается. Первым делом появляется приветствие:



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



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

Однако более меня всё же интересует иной момент: меняем код метода 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 уже не будет сразу открываться документ, как в предыдущем случае. Вместо этого видим следующее:



Однако отладчик показывает, что 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. Почему при открытии модального окна наблюдается иное поведение (первый вариант кода)?

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
1. Интересует приемлемое решение обозначенной проблемы.
Вопрос передам в ADN DevHelp, хотя я думаю что ты сам догадался, что видимо начиная с AutoCAD 2015 в консоль можно будет выводить не всегда. Впрочем в ObjectARX такое уже было. Кстати, кое что может зависеть и от состояния системной переменной STARTUP.
2. Почему при открытии модального окна наблюдается иное поведение (первый вариант кода)?
А что тебе в этом удивляет? Модальное окно никак не привязано к документу и соответственно к консоли AutoCAD
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
А что тебе в этом удивляет? Модальное окно никак не привязано к документу и соответственно к консоли AutoCAD
Меня не то, чтобы удивляет, но несколько напрягает тот факт, что содержимое расширения может непреднамеренно повлиять на состояние AutoCAD при его инициализации. В одном случае на конечном этапе мы получаем уже открытый документ, а во втором - нет. Считаю такое поведение некорректным (имхо).

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

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

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Как вариант, можно подключиться на событие DocumentCreated и в обработчике выводить информацию в консоль создаваемого документа. Однако в том случае, если сборка загружена не при помощи автозагрузчика AutoCAD, но вручную, при помощи NETLOAD, юзер снова не увидит это сообщение (в текущем документе), но вместо этого он увидит его лишь в очередном созданном документе, что опять не есть хорошо.
А если подписаться на DocumentLockModeWillChange? Ну само собой сразу отписаться после первого вывода сообщения.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
А если подписаться на 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. }

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
А что толку? Ведь будет это происходить всё в том же "призраке" 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.  

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
А такой вариант не устроит?:
Не устроит. Расширение загружается один раз. Соответственно и сообщение о загрузке должно выводиться в консоль один раз. Выводя сообщение о загрузке в консоль каждого документа, ты тем самым создаёшь у пользователя иллюзию многократной загрузки расширения - это неприемлемо.

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Оно и будет один раз в консоли для каждого документа. Расширение же и для него загружено тоже - оно не в документ же грузится.

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
будет один раз в консоли для каждого документа.
Но правильней - в консоль только текущего.

Оффлайн Дима_

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
Рисовать на 5 секунд свое окно без обрамления и прочего и плавно его гасить...

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Рисовать на 5 секунд свое окно без обрамления и прочего и плавно его гасить...
Можно, конечно, рюшечки придумывать, но всё же хочу так, как обычно делаю: именно в консоль текущего документа (и только его). В общем, я так понимаю, это очередное проявление одного из множества багов, которые в каждой новой версии AutoCAD множатся как черви. Титаник медленно идёт ко дну.

Оффлайн Дима_

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
Я так понимаю автодеск решил потихоньку "отползать" от обязательства консоли, но до конца еще не придумал как.

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

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Но правильней - в консоль только текущего.
Почему? Предположим, там выводится:
"Приложение супер-пупер загружено. Запускать командой ЗАПУСТИСУПЕРПУПЕРПРИЛОЖЕНИЕ"
Чтобы увидеть эту информацию, пользователю нужно переключаться на первоначальный документ? А если он уже закрыт?
Опять же, не каждый пользователь знает, загружено у него приложение в документ или нет. Лиспы, например, грузятся только в текущий документ. А сообщение дает понять, что именно это в новый документ дополнительно грузить не надо.
Если же надо вывести сообщение одно на все приложение - поддержу Дима_, логичнее вывести "приветственное окошко".

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78