Главная »Статьи »AutoCAD и Delphi » |
Технология OLE (ну, или по-современному — COM) привнесла в нашу жизнь много интересного, полезного и загадочного… В статье Подключаемся… я показал отправные точки для установления связи между AutoCAD и Delphi. Основными здесь являются такие функции как GetActiveObject и CreateComObject принимающие в качестве агрумента ClassName либо GUID. Автономное приложение Delphi эффективно работает, используя данные методы если:
В случаях, если:
метод подключения, основанный на ClassName или GUID может давать сбои (выражающиеся хотя бы в том, что пользователь ожидает результат Delphi-приложения в одном экземпляре AutoCAD, а получает его совсем в другом). Существует ряд задач, когда необходимо определенно знать экземпляр AutoCAD, для которого предназначен результат Delphi-приложения. К таким задачам, прежде всего, следует отнести расширение возможностей AutoCAD, когда для него создаются новые команды, запрашивающие выбор пользователя и на его основе выполняющие необходимые построения. Средствами создания новых команд являются:
Поскольку поклонником Lisp я явно не являюсь, приходится использовать VBA (он все же ближе к родному Delphi). Но если использовать VBA зачем Delphi? Ведь в VBA можно и окошки создать и примитивы отрисовать. Так то так, но… есть "скрытое обаяние Delphi". Итак, архитектура приложения складывается так: макрос VBA вызывает функцию из Delphi DLL (обычно с диалогом), которая выполняет всю работу. И тут мы подходим ко второй предпосылке — передать данные (о слоях, текстовых стилях и т.д.) из VBA в DLL. На первый взгляд все просто. Макрос VBA берет у AutoCAD необходимые данные и записывает в файл. DLL при старте читает их. Но:
А отсюда следует, что DLL должна возвратить выбор пользователя опять через файл, макрос прочтет его и отрисует примитивы. Ну и для чего нам Delphi? Окошки делать? Можно конечно попытаться сформировать в VBA массив данных и передать их как Variant в Delphi, но первый вопрос все равно остается не решенным. Следовательно, задача такова: найти механизм связи между AutoCAD и Delphi однозначно идентифицирующий "свой" экземпляр AutoCAD и решающий вопрос передачи данных в Delphi приложение без промежуточных файлов. КонцепцияСправка VBA по объекту Application гласит: "For AutoCAD VBA: Not applicable. The application is always available". Т.е. Application всегда доступен — его и надо передавать в DLL. Программа на VBA должна содержать:
Delphi DLL должна:
РеализацияСоздадим приложение, вызываемое кнопкой на панели инструментов AutoCAD, показывающее диалог, демонстрирующий список слоев, при нажатии кнопки запрашивающее у пользователя две точки и рисующее прямую между данными точками на выбранном слое. Вид диалога:
Текст "TestDLL.dvb": Option Explicit ' Объект передаем по ссылке (из справки: ByRef is the default in Visual Basic) Private Declare Function DLL_Init Lib "TestDLL.dll" (VBAcad As Variant) As Boolean ' Основная функция диалога Private Declare Function ShowTestDlg Lib "TestDLL.dll" () As Boolean Public Sub ShowTest() Dim A As AcadApplication Set A = ThisDrawing.Application If DLL_Init(A) Then ' Инициализация DLL ShowTestDlg ' Вызов основной функции End If End Sub Перед вызовом пользовательской функции — ShowTestDlg производится вызов специальной функции DLL_Init. Если Delphi DLL "опознала" переданный ей AcadApplication, она должна вернуть True и, если это так, то можно вызывать другие пользовательские функции из DLL. Текст макроса для кнопки: ^C^C-vbarun TestDLL.dvb!ShowTest Текст "TestDLL.dpr" (проект DLL): library TestDLL; uses SysUtils, Classes, AcDvbCommon in 'AcDvbCommon.pas', UfrmTest in 'UfrmTest.pas' {frmTest}, AcCommon in 'AcCommon.pas'; exports DLL_Init, ShowTestDlg; begin end. Модуль AcDvbCommon содержит экспортируемую функцию DLL_Init и должен быть первым (из пользовательских) в списке. Модуль UfrmTest содержит диалог и экспортируемую функцию ShowTestDlg. Ну а в модуль AcCommon я сложил разные вспомогательные функции и константы, чтобы не засорять другие модули. Текст AcDvbCommon.pas: unit AcDvbCommon; interface uses ComObj, Variants, Forms, ActiveX; function DLL_Init(var VBAcad: OleVariant): WordBool; stdcall; var Acad: OleVariant; implementation function DLL_Init(var VBAcad: OleVariant): WordBool; begin Result:= not VarIsClear(Acad); if not Result then begin try if VariantCopyInd(Acad, VBAcad) = S_OK then begin Application.Handle:= Acad.HWND; Result:= True; end; except VariantClear(Acad); Result:= False; end; end; end; initialization finalization if not VarIsClear(Acad) then begin VariantClear(Acad); end; end. В функции DLL_Init, если Acad еще не инициализирована выполняется ее инициализация, а затем Application.Handle присваивается Handle главного окна AutoCAD. А если это не первый вызов функции и Acad уже инициализирована — возвращается True. В секции finalization Acad освобождается. Остальные методы тестового приложения не содержат особенностей и поэтому здесь не приводятся. Подробнее см. исходники, прилагаемые к статье. И в заключение, снимок с экрана с двумя одновременно запущенными AutoCAD и вызванными диалогами:
К статье прилагаются примеры на Delphi 7. Внимание! Запрещается воспроизведение
данной статьи или ее части без согласования с автором. Если вы желаете разместить
эту статью на своем сайте или издать в печатном виде, свяжитесь с автором. |