Главная »Статьи »AutoCAD и Delphi »Подключаемся…
Подключаемся…

Способ №1 – раннее связывание.

В Delphi из меню Project выберите Import Type Library…, в окне Import Type Library нажмите Add и укажите acad.tlb в каталоге AutoCAD (для AutoCAD 2000 — 2002) или acax16enu.tlb в каталоге …Common Files\Autodesk Shared (для AutoCAD 2004 — 2006). Нажмите Install и установите предлагаемые компоненты.

Итак, для AutoCAD 2002 на палитре ActiveX появятся компоненты: TAcadDatabase, TAcadDocument, TAcadLayerStateManager из которых наибольшую ценность представляет TAcadDocument. Теперь, когда подготовительные операции завершены, начните новое приложение, добавьте на форму компонент TAcadDocument, поставьте его свойство AutoConnect в True, добавьте кнопку и на ее событие Click следующий код:

var
  StartPoint, EndPoint: OleVariant;
begin
  // начальная точка отрезка
  StartPoint:= VarArrayCreate([0, 2], varDouble);
  StartPoint[0]:= 0; // X-координата
  StartPoint[1]:= 0; // Y-координата
  StartPoint[2]:= 0; // Z-координата
  // конечная точка отрезка
  EndPoint:= VarArrayCreate([0, 2], varDouble);
  EndPoint[0]:= 100;
  EndPoint[1]:= 100;
  EndPoint[2]:= 0;
  // добавление отрезка
  AcadDocument1.ModelSpace.AddLine(StartPoint, EndPoint);
  AcadDocument1.Application.Update;
end;

При запуске созданного примера происходит присоединение к запущенному экземпляру AutoCAD или загрузка, если AutoCAD не был ранее запущен. Нажмите кнопку – AutoCAD нарисует отрезок. Вот собственно и все…

Импортирование и использование библиотеки типов удобно по нескольким причинам:
  1. чтобы иметь представление о том, как объектная модель AutoCAD выглядит на Object Pascal;
  2. чтобы иметь возможность использовать механизм Code Completion;
  3. чтобы узнать значения констант;
  4. чтобы иметь доступ к событиям документа.

Имеется и недостаток: вы всегда жестко привязаны именно к той версии AutoCAD, от которой взяли tlb за счет включенных в Type Library GUID. По этой причине далее я не буду использовать библиотеку типов.

Важно!

В основновном по этой причине приводимые в статьях определения методов и свойств не всегда имеют типы данных, совпадающие с определенными в импортированной AutoCAD Type Library. Так, например, в AutoCAD_TLB.pas определение метода AddLine таково:
function AddLine(StartPoint: OleVariant; EndPoint: OleVariant): IAcadLine; safecall;
Я буду приводить его как:
function AddLine(StartPoint: OleVariant; EndPoint: OleVariant): OleVariant;

Способ №2 – позднее связывание. Для его использования не требуется библиотека типов, но нужно хорошее знание объектной модели AutoCAD и в этом помогает справка: acad.chm => ActiveX and VBA => ActiveX and VBA Reference => Object Model. Подключение можно выполнить с помощью такой функции:

uses
  ComObj, ActiveX;
  
resourcestring
  rsAcadNotFound = 'AutoCAD не найден!';

var
  Acad: OleVariant;
  
procedure AcConnect(const acClassName: String; const acNewInstance: Boolean;
    const acVisible: Boolean);
var
  IU: IUnknown;
  ClassID: TCLSID;
  IsCreate: Boolean;
  ErrorCode: HResult;
begin
  if VarIsClear(Acad) then // если еще не присоединились… 
  begin
    // если AutoCAD не установлен — произойдет ошибка
    ErrorCode:= CLSIDFromProgID(PWideChar(WideString(acClassName)), ClassID);
    if not Succeeded(ErrorCode) then
      raise EOleSysError.Create(rsAcadNotFound, ErrorCode, 0);
    // пытаемся присоединиться к уже запущенному экземпляру AutoCAD
    IsCreate:= acNewInstance or (not Succeeded(GetActiveObject(ClassID, nil, IU)));
    if IsCreate then
      try
        // запускаем новый экземпляр AutoCAD
        Acad:= CreateComObject(ClassID) as IDispatch;
      except
        raise EOleSysError.Create(rsAcadNotFound, ErrorCode, 0);
      end
    else
      Acad:= IU as IDispatch;
    // управляем видимостью AutoCAD
    if not VarIsClear(Acad) then Acad.Visible:= acVisible;
  end;
end;

Где-то в программе:

AcConnect('AutoCAD.Application', True, False);
Сделаем его видимым...

Но сначала посмотрим как спрятать:

procedure AcHide;
begin
  if not VarIsClear(Acad) then Acad.Visible:= False;
end;

А теперь покажем. Вот здесь начинаются хитрости. Казалось бы надо просто написать:

Acad.Visible:= True;

Я делаю так:

procedure AcShow(WndState: AcWindowState = acMax);
begin
  if not VarIsClear(Acad) then
  begin
    Acad.Visible:= True;
    Acad.WindowState:= WndState;
  end;
end;

Зачем нужен Acad.WindowState:= WndState? Закомментируйте эту строку и запустите пример. Нажимайте кнопки Connect, Show, Hide. Все работает. Сделайте его видимым, затем минимизируйте. И нажмите Show — AutoCAD не вернется из минимизированного состояния. Можете нажимать Hide и Show — это будет приводить только к скрытию/появлению кнопки AutoCAD в панели задач. Вот это и исправляет Acad.WindowState:= WndState. Константа acMax взята из AutoCAD_TLB.pas, так что говоря выше, что не буду использовать библиотеку типов я немного слукавил. Cовсем отказаться от нее не получится.

Тип AcWindowState определен в AutoCAD_TLB.pas как TOleEnum. Сам же TOleEnum определен в ActiveX.pas. В Delphi 5 он был определен как Integer, что неверно и создавало трудности, например при задании весов линий (LineWeight) по-слою, когда LineWeight = acLnWtByLayer ($FFFFFFFF). В Delphi 7 TOleEnum верно определен как синоним LongWord.
Закроем AutoCAD
procedure AcRelease;
begin
  if not VarIsClear(Acad) then
  begin
    Acad.Quit;
    Acad:= Unassigned;
  end;
end;
Примечания:
  • Перед обращением к переменной Acad я всегда проверяю ее с помощью VarIsClear. Это помогает избавиться от ошибок когда подключения еще не было. Но если вы запустили AutoCAD, присоединились и закрыли (через меню "File/Exit" или крестиком), то проверка VarIsClear не предотвращает ошибку и обращение к переменной Acad вызывает EOleSysError with message "Сервер RPC не доступен".
  • Если в AutoCAD открыто модальное окно, то обращение к переменной Acad или попытка подключения вызывает EOleSysError with message "Вызов был отклонен".
Они размножаются...

Если AutoCAD-ов несколько, то в функции AcConnect параметру acClassName необходимо задать соответствующее значение имени класса. Мне известны такие версии под Windows:

Наименование
Дата
Имя класса
1
AutoCAD Release 13 Ноябрь 1994 AutoCAD.Application.13
2
AutoCAD Release 14 Май 1997 AutoCAD.Application.14
3
AutoCAD 2000 Март 1999 AutoCAD.Application.15
4
AutoCAD 2000i Июль 2000 AutoCAD.Application.15.01
5
AutoCAD 2002 Июнь 2001 AutoCAD.Application.15.02
6
AutoCAD 2004 Март 2003 AutoCAD.Application.16
7
AutoCAD 2005 Март 2004 AutoCAD.Application.16.1
8
AutoCAD 2006 Март 2005 AutoCAD.Application.16.2

Если имя класса задать AutoCAD.Application, то присоединение будет производится к той версии, которая была использована последней. Выяснить какая версия последняя, можно в реестре по ветке: HKEY_CURRENT_USER\Software\Classes\AutoCAD.Application. Для этого пригодится функция:

function GetCurrAutoCADClassName: String;
var
  reg: TRegistry;
  res: Boolean;
begin
  Result:= EmptyStr;
  reg:= TRegistry.Create;
  try
    with reg do
    begin
      RootKey:= HKEY_CURRENT_USER;
      res:= OpenKeyReadOnly('\Software\Classes\AutoCAD.Application\CurVer');
      if not res then Exit;
      Result:= ReadString('');
    end;
  finally
    FreeAndNil(reg);
  end;
end;

Чтобы узнать какие версии AutoCAD установлены, необходимо в реестре смотреть ветку HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\AutoCAD. Для этого предлагается следующая функция:

function EnumAutoCAD(Items: TStrings): Boolean;
var
  reg: TRegistry;
begin
  reg:= TRegistry.Create;
  try
    with reg do
    begin
      RootKey:= HKEY_LOCAL_MACHINE;
      Result:= OpenKeyReadOnly('\SOFTWARE\Autodesk\AutoCAD');
      if not Result then Exit;
      GetKeyNames(Items);
    end;
  finally
    FreeAndNil(reg);
  end;
end;

Для AutoCAD 2002 и AutoCAD 2006 она возвращает следующий список:
R15.0
R16.2

Для преобразования символических имен в имена класса пригодится функция:

function AutoCADFamilyNameToClassName(const AFamilyName: String): String;
var
  p: Integer;
begin
  Result:= AFamilyName;
  p:= Pos('.', Result);
  if Copy(Result, p, MaxInt) = '.0' then
    Delete(Result, p, MaxInt);
  Result:= StringReplace(Result, 'R', 'AutoCAD.Application.', [rfIgnoreCase]);
end;

Иногда нужно достать до настроек AutoCAD, профилей. Все это лежит в ветке HKEY_CURRENT_USER\Software\Autodesk\AutoCAD, однако добраться туда не так просто. Дело в том, что внутри ветки AutoCAD лежит ветка R15.0, а внутри ее, например, ACAD-1:409 и уже там заветная ветка Profiles. Для AutoCAD 2006 путь может быть таким: \R16.2\ACAD-4001:409\Profiles. Имена веток ACAD-XX:YY могут меняться. Значения XX:YY, в моем случае, формируются по принципу ProductId:LocaleId. Чтобы знать их наверняка нужно читать ветку: HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk\Hardcopy, например такой функцией:

function EnumAutoCADHardcopy(Items: TStrings): Boolean;
var
  reg: TRegistry;
begin
  reg:= TRegistry.Create;
  try
    with reg do
    begin
      RootKey:= HKEY_LOCAL_MACHINE;
      Result:= OpenKeyReadOnly('SOFTWARE\Autodesk\Hardcopy');
      if not Result then Exit;
      GetValueNames(Items);
    end;
  finally
    FreeAndNil(reg);
  end;
end;

Функция возвращает относительные пути к веткам, например:
Autodesk\AutoCAD\R15.0\ACAD-1:409
Autodesk\AutoCAD\R16.2\ACAD-4001:409

К статье прилагаются примеры на Delphi 7.


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

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