AutoCAD 2015 SP1.
Как известно, начиная с версии R20.0 (AutoCAD 2015) свойство MdiActiveDocument может оказаться равным null, в виду чего следует всегда выполнять соответствующую проверку. Однако оказывается, что это ещё не все "пряники"...
Код:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Windows.Forms;
#if AUTOCAD
using cad = Autodesk.AutoCAD.ApplicationServices.Application;
using Ap = Autodesk.AutoCAD.ApplicationServices;
using Db = Autodesk.AutoCAD.DatabaseServices;
using Ed = Autodesk.AutoCAD.EditorInput;
using Rt = Autodesk.AutoCAD.Runtime;
using Us = Autodesk.AutoCAD.DatabaseServices.SymbolUtilityServices;
using Hs = Autodesk.AutoCAD.DatabaseServices.HostApplicationServices;
#endif
[assembly: Rt.ExtensionApplication(typeof(AcadHelloFromBundle.Class1))]
[assembly: Rt.CommandClass(typeof(AcadHelloFromBundle.Class1))]
namespace AcadHelloFromBundle {
public sealed class Class1 : Rt.IExtensionApplication {
void WriteMessage(String methodName) {
String message = String.Format(
"\nHello from the \"{0}\" method!\n", methodName);
MessageBox.Show(message, "Hello", MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
[Rt.CommandMethod("TEST", Rt.CommandFlags.Session)]
public void Test() {
Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
using (doc.LockDocument()) {
WriteMessage(MethodBase.GetCurrentMethod().Name);
}
}
#region IExtensionApplication Members
public void Initialize() {
Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
if (null == doc) {
cad.DocumentManager.DocumentActivated += DocActivated;
}
else {
using (doc.LockDocument()) {
WriteMessage(MethodBase.GetCurrentMethod().Name);
}
}
}
void DocActivated(object sender, Ap.DocumentCollectionEventArgs e) {
cad.DocumentManager.DocumentActivated -= DocActivated;
Ap.Document doc = e.Document;
using (doc.LockDocument()) {
WriteMessage(MethodBase.GetCurrentMethod().Name);
}
}
public void Terminate() {
// throw new NotImplementedException();
}
#endregion
}
}
Сборка зарегистрирована в реестре (ветка HKCU). При старте AutoCAD 2014, код успешно загружается. Первым делом появляется приветствие:
При этом пока не появились ни вкладки документов, ни риббоны. Обращаю внимание на то, что в методе Initialize объект MdiActiveDocument уже не равен null. Так же обратите внимание на то, что на заднем фоне видим фон уже открытой Model (хотя сама вкладка ещё не появилась). И лишь после нажатия ОК, появляются все недостающие причиндалы:
То, что риббоны и вкладки документов появляются позднее - это (имхо) не смертельно и обозначается лишь как дополнительная информация. Вопрос, касающийся данной ситуации будет обозначен ниже.
Однако более меня всё же интересует иной момент: меняем код метода WriteMessage следующим образом:
void WriteMessage(String methodName) {
String message = String.Format(
"\nHello from the \"{0}\" method!\n", methodName);
//MessageBox.Show(message, "Hello", MessageBoxButtons.OK,
// MessageBoxIcon.Information);
Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
if (null == doc || doc.IsDisposed)
throw new Exception("Invalid MdiActiveDocument.");
doc.Editor.WriteMessage(message);
}
В данном случае при старте AutoCAD 2015 уже не будет сразу открываться документ, как в предыдущем случае. Вместо этого видим следующее:
Однако отладчик показывает, что MdiActiveDocument и в этом случае не null, сообщение успешно отправляется в консоль. Вот только в какую? Отладчик показывает, что объект, на который указывает MdiActiveDocument имеет имя Drawing1.dwg. Но чертежи ведь не открыты, как видим на скрине... Если теперь создать новый чертёж, то он так же автоматом получает имя Drawing1.dwg.
Хорошо, вполне возможно, что первый чертёж Drawing1.dwg временно создаётся и затем убивается к тому времени, как будет отображено то, что я показал на скрине. После его удаления имя может быть использовано повторно (что AutoCAD 2015 и делает). Проверяю предположение:
public void Initialize() {
cad.DocumentManager.DocumentDestroyed += DocDestroyed; // Добавил это
Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
if (null == doc) {
cad.DocumentManager.DocumentActivated += DocActivated;
}
else {
using (doc.LockDocument()) {
WriteMessage(MethodBase.GetCurrentMethod().Name);
}
}
}
void DocDestroyed(object sender, Ap.DocumentDestroyedEventArgs e) {
// На вызов этого метода ставим брэйкпоинт.
}
Запускаю код и вижу, что брэйкпоинт в методе DocDestroyed действительно срабатывает до того, как появляется предыдущий мой скрин - т.е. моё предположение оказалось верным. Однако нельзя сказать, что это меня радует...
Зачастую, при загрузке моих сборок я вывожу в консоль текущего документа некоторый текст, сигнализирующий о том, что загрузка прошла успешно. Там же может находится некоторая подсказка для пользователя: например перечень доступных команд. Однако в данном случае получается, что при автозагрузке мой текст может отправиться вовсе не туда, куда нужно и юзер не увидит обозначенную информацию. Это достаточно неудобно.
Как вариант, можно подключиться на событие DocumentCreated и в обработчике выводить информацию в консоль создаваемого документа. Однако в том случае, если сборка загружена не при помощи автозагрузчика AutoCAD, но вручную, при помощи NETLOAD, юзер снова не увидит это сообщение (в текущем документе), но вместо этого он увидит его лишь в очередном созданном документе, что опять не есть хорошо.
Далее собственно вопросы:1. Интересует приемлемое решение обозначенной проблемы.
2. Почему при открытии модального окна наблюдается иное поведение (первый вариант кода)?