Выполнение кода сразу после загрузки .NET-dll
Иногда бывает необходимо до вызова команд (а еще лучше - прямо после загрузки кода) выполнить некоторые действия: установить системные переменные, установить пути поддержки и т.п.). В лиспе это просто:
1 2 3 4 5 6 | (defun test() (alert "test") ) ;; А вот и выполнение (test) |
Это то, что в лиспе часто называют "самовызовом". Выполнение кода начинается сразу после загрузки соответствующего lsp-файла в память.
Механизм достаточно удобный, несмотря на некоторые трудности, описанные здесь и здесь.
С .NET ситуация немного иная.
Для решения этой задачи в .NET-приложении должен быть определен специальный класс. К этому классу предъявляется несколько требований:
- Класс такого вида может быть только один внутри всей dll (насколько я понял)
- Класс должен быть объявлен как IExtensionApplication
- В классе должны присутствовать функции с именами Initialize() и Terminate()/ Уже по именам функций понятно, что они должны делать и в какой момент времени вызываются
- Функции Initialize() и Terminate() не могут быть объявлены статическими
Приведу сразу код (один достаточно скользкий момент чуть позже):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | using System; using System.Windows.Forms; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.EditorInput; namespace InitializationTest { public class InitTest : Autodesk.AutoCAD.Runtime.IExtensionApplication { public void Initialize() { MessageBox.Show("initialization test", "test", MessageBoxButtons.OK, MessageBoxIcon.Information); Editor ed = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor; ed.WriteMessage("\ninitialization test start..."); } public void Terminate() { Console.WriteLine("finish!"); } } } |
В общем-то все достаточно просто. Но! Обратите внимание, в функции Terminate() не используется объект Editor.
Объект Editor отсутствует потому, что функция Terminate() вызывается в тот момент, когда AutoCAD уже закрыт (ну или по крайней мере закрывается ;)), и получить доступ к Editor по меньшей мере затруднительно. Скажу честно: я попробовал сразу после Console.WriteLine поставить
1 |
MessageBox.Show("terminate", "test", MessageBoxButtons.OK, MessageBoxIcon.Information); |
Но сообщение не выводилось (подозреваю, что это связано именно с закрытием приложения-владельца, но не уверен).
Зачем может понадобиться вызов Terminate()? Да все просто. Например, гарантированно закрыть все открытые файлы, коннекты к базам данных или Internet и т.д..
Кроме всего прочего, теоретически можно в этом же классе прописывать и пользовательские команды. Но лично у меня при работе в AutoCAD 2008 команды "не находились", несмотря на корректно установленные параметры acdbmdg.dll и acmgd.dll. И опять-таки: этот класс не может содержать статические члены, соответственно dll придется загружать в каждый файл. Не очень приятное (для меня) известие.
Добавлено:
И еще один момент. Пока один. Первая загрузка вызывает Initialize(), что в общем-то ожидаемо. Но вот повторная загрузка (лично у меня) код инициализации не вызвала. Как побороть - пока не знаю.
P.S. За основу взят пост с thought-the-interface
>Но вот повторная загрузка (лично у меня) код инициализации не вызвала. Как побороть – пока не знаю.
А повторная загрузка и не происходит (попытки игнорируются). Поэтому и нет повторного вызова Initialize(). В домен (AppDomain) можно одну и ту же сборку загрузить только один раз. Выгрузить сборку нельзя, вместо этого следует выгружать домен, которых можно создавать в .NET приложениях столько, сколько потребуется. Однако, поскольку в AutoCAD этот момент реализован через задницу, то там все плагины грузятся в один единый домен. Т.е. выгрузка .NET плагинов в AutoCAD невозможна.
Это одна из причин, по которым я стараюсь загрузку выполнять через mnl / lsp - по крайней мере они грузятся в каждый dwg
>Это одна из причин, по которым я стараюсь загрузку выполнять через mnl / lsp – по крайней мере они грузятся в каждый dwg
В dwg ничего не грузится. В AutoCAD существует два контекста: контекст приложения и контекст документа. То, что ты назвал "загрузкой в dwg" - это контекст приложения. LISP плагины грузятся в контекст документа. Функции, загруженные в контекст документа, доступны только из этого документа. Код, загруженный в контекст приложения, доступны во всех документах (как уже открытых, так и тех, что будут открыты в данной сессии позднее).
Плагины .NET грузятся в контекст приложения. Плагины ARX могут быть загружены как в контексте приложения, так и в контексте документа (на усмотрение программиста).
>То, что ты назвал “загрузкой в dwg” – это контекст приложения.
Очепятка, хотел написать "- это контекст документа".
Т.о. нет смысла пытаться грузить .NET библиотеку повторно, даже если бы это и было возможно: код и так будет доступен во всех документах, доступных в рамках текущей сессии AutoCAD. Следовательно твои опасения напрасны.
Ну да, контекст документа, если использовать терминологию .NET / ObjecARX программирования
Добавлю: нередко приходится загружать arx-сборки. А это уже касается и контекста документа.
>Добавлю: нередко приходится загружать arx-сборки. А это уже касается и контекста документа.
Я же писал, что контекст зависит от программиста, написавшего код:
>Плагины ARX могут быть загружены как в контексте приложения, так и в контексте документа (на усмотрение программиста).
Согласен. Но предсказать такое, мне кажется, достаточно проблематично. Тот же самый dwgconvert от Александра Ривилиса грузится в контекст документа, мне кажется.
>Но предсказать такое, мне кажется, достаточно проблематично.
Ну да, это только путём проверки на практике. Однако это не означает, что в качестве перестраховки нужно все библиотеки подряд пытаться подгрузить в контекст каждого документа в рамках текущей сессии AutoCAD. Просто по хорошему, контекст загрузки должен быть указан программистом в текстовом описании (обычно это файл readme.txt), поставляемом совместно с библиотекой. В этом случае пользователь, или CAD администратор, сразу поймёт, "достаточно ли одной таблетки".
Хотя, я не знаю, какое поведение реализовано в Startup Suite для зарегистрированных в нём ARX приложений: возможно он определяет контекст ARX модуля, и исходя из этого либо грузит библиотеку единожды, либо вообще не парится с этим вопросом и грузит в контекст каждого документа. Можно было бы заморочиться с проверкой этого вопроса но, честно говоря, мне это не шибко интересно