![]() |
| Главная »Статьи »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. Внимание! Запрещается воспроизведение
данной статьи или ее части без согласования с автором. Если вы желаете разместить
эту статью на своем сайте или издать в печатном виде, свяжитесь с автором. |