Главная »Статьи »AutoCAD и Delphi »Снова о подключении Delphi к AutoCAD или зачем нам VBA?
Снова о подключении Delphi к AutoCAD или зачем нам VBA?

Технология OLE (ну, или по-современному — COM) привнесла в нашу жизнь много интересного, полезного и загадочного…

В статье Подключаемся… я показал отправные точки для установления связи между AutoCAD и Delphi. Основными здесь являются такие функции как GetActiveObject и CreateComObject принимающие в качестве агрумента ClassName либо GUID. Автономное приложение Delphi эффективно работает, используя данные методы если:

  • оно запускает "свой", единственный экземпляр AutoCAD;
  • оно подключается к единственному запущенному экземпляру AutoCAD.

В случаях, если:

  • на машине присутствуют разные версии AutoCAD и пользователь запустил их обе;
  • пользователь запустил несколько экземпляров AutoCAD одной версии

метод подключения, основанный на ClassName или GUID может давать сбои (выражающиеся хотя бы в том, что пользователь ожидает результат Delphi-приложения в одном экземпляре AutoCAD, а получает его совсем в другом).

Существует ряд задач, когда необходимо определенно знать экземпляр AutoCAD, для которого предназначен результат Delphi-приложения. К таким задачам, прежде всего, следует отнести расширение возможностей AutoCAD, когда для него создаются новые команды, запрашивающие выбор пользователя и на его основе выполняющие необходимые построения.

Средствами создания новых команд являются:

  • специальные функции Lisp:
  • макросы VBA.

Поскольку поклонником Lisp я явно не являюсь, приходится использовать VBA (он все же ближе к родному Delphi). Но если использовать VBA зачем Delphi? Ведь в VBA можно и окошки создать и примитивы отрисовать. Так то так, но… есть "скрытое обаяние Delphi". Итак, архитектура приложения складывается так: макрос VBA вызывает функцию из Delphi DLL (обычно с диалогом), которая выполняет всю работу. И тут мы подходим ко второй предпосылке — передать данные (о слоях, текстовых стилях и т.д.) из VBA в DLL.

На первый взгляд все просто. Макрос VBA берет у AutoCAD необходимые данные и записывает в файл. DLL при старте читает их. Но:

  • для отрисовки DLL ничего не знает про "свой" экземпляр AutoCAD;
  • код макроса VBA вырос и стал не просто вызовом 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 для ее инициализации. Параметром этой функции будет экземпляр AcadApplication. Возвращаемое функцией значение должно свидетельствовать о результате инициализации DLL;
  • вызовы пользовательских функций из Delphi DLL.

Delphi DLL должна:

  • при вызове специальной функции получить Application;
  • используя Application запросить AutoCAD необходимые данные;
  • показать пользователю окно выбора параметров (если необходимо);
  • используя Application выполнить отрисовку примитивов.

Реализация

Создадим приложение, вызываемое кнопкой на панели инструментов 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.


Внимание! Запрещается воспроизведение данной статьи или ее части без согласования с автором. Если вы желаете разместить эту статью на своем сайте или издать в печатном виде, свяжитесь с автором.
Автор статьи: Вершинин И.В.

 
Используются технологии uCoz