Форумы caduser.ru

  5
Поиск  Пользователи  Правила 
Закрыть
Логин:
Пароль:
Забыли свой пароль?
Регистрация
Войти  
Страницы: 1
RSS
Программирование AutoCAD из СИ-программы, Доступ к объектной модели AutoCAD из внешней программы
Вопрос о том, как "достучаться" до АутоКАДа "снаружи" и из языков программирования иных, чем ВижуалБэйсик, периодически возникает уже не менее 15-ти лет на разных наших и не наших форумах.
Реальные успехи имели поклонники Дельфи-Паскаль.
А вот СИ-шникам все эти годы внушалось, что без "гравицапы" по имени ObjectARX от АутоДеск никак нельзя.
Но такая "гравицапа" живет только 3 года, а потом надо покупать новую - еще более ... и т.д.
Но и она "летает только в своей кентурии", то есть внутри АутоКАДа (также как АутоЛИСП, как VBA).

Единственным исключением был ВижуалБэйсик (последнее обновление от 1998 года).
Он и сейчас вполне пригоден для означенной задачи, но ведь MS в любое время может выкинуть его главную библиотеку из системы и заплачут "пацаки".

Интерес к означенной теме можно обнаружить даже у корефанов Питона (Python).
У них "всё получилось", но даже с 'экзешником' полученным из Питона через их утилиту 'Py2Exe' надо носить с машины на машину 2-4 МБ поддерживающих библиотек.
А наличие/отсутствие некоторых MS библиотек (конкретной версии компилятора VC) в системе, может помешать успешной дружбе этих двух разных цивилизаций.

Все что надо для программирования АутоКАДа это:
CLSID_AcadApplication
CLSID_AcadDocument
CLSID_AcadModelSpace
Препятствием является отсутствие в Windows-Registry 'CLSID' для AutiCAD.Docunent и AutiCAD.ModelSpace, там есть только для AutiCAD.Application.
И зто не позволяет программисту получить в свое распоряжение эти ключевые объекты (после главного AutiCAD.Application).
И это при том, что вся (почти вся) объектная модель АутоКАДа находится в файле acax[NN]enu.tlb (где [NN] - цифры, зависящие от версии АутоКАДа).
Такого препятствия нет при обращении к изделиям MS, например MS-Office, а также к изделиям многих других производителей программ.
Особенно, если они заинтересованы в создании на стороне пользователей различных дополнений/улучшений их программ.
Вот когда у AD и MS "была любоф", тогда от MS появился 'VBA-AutoCAD'.
Но как и многое другое у них он был "немного недоношенный". А потом "любоф" закончилась и VBA-AutoCad стал опцией по требованию со стороны пользователя.

Все дело в том, что использование TLB и некоторых DLL библиотек происходит по 'COM-OLE-Automation' протоколам.
Над 'COM-OLE' и над повсемесным их внедрением немало потрудились в MS.
Но для хитро===== программистов в этих протоколах и прежде всего в самой ОС-Виндовс есть известные им и MS способы "не пущать". Для этого есть вполне прагматические мотивы.

После серии проб и ошибок, получен (не без помощи "наших немецких EURO-товарищей") вполне себе рабочий пример (так называемый "simple-example").
Написан он на "ПРОСТО-СИ", даже не "СИ-Пляс-Пляс" - для компиляции использован Pelles-C, он понятный и бесплатный.
Создание в чертеже объекта 'AutoCAD.Line', доказывает что программа "достучалась" до объектной модели АутоКАДа.
Дальше можно ее наращивать в нужном направлении, создавая вызовы необходимых функций АутоКАДа (а также Windows-системы).

Поклонники MS-VS-C++ вероятно знают для чего бывают нужны строки типа:
Код
EXTERN_C const CLSID CLSID_ErrObject;

и они таки вставят их в нужном месте, если не хотят переделывать код из СИ в СИ++ для компиляции в MS-VS.

Поскольку СИ (не СИ++) компиляторы не работают с TLB-библиотеками напрамую, как в C++:
Код
#import "C:\\Program Files\\Common Files\\Autodesk Shared\\acax17enu.tlb"

постольку для них из TLB-файла (бинарного) компилируеся H-файл (он текстовый и из него много чего можно вычитать).
Для этого умные люди сделали утилиту 'FtypeLib.exe', чем я и воспользовался.
И получил и положил в папку проекта файл acax[XX]enu.h который заставляет работать acax[XX]enu.tlb нужным для программы образом, игнорируя отсутствие прописанных в Windows-Registry столь необходимых классов/объектов АутоКАДа.

Попутно надо отметить, что в менее известном (у нас), но не менее функциональном IntelliCAD (он же ProgeCAD) таких проблем для программистов нет.
Все необходимые для программиста классы/объекты доступны в его программе сразу и без "камлания".
При этом объектная модель, "начинка" и даже "морда" у них, как у близнецов.

Ребята из "АвтоматическогоСтола" как и из "МелкогоМягкого" постоянно работают над улучшением и в связи с этим также постоянно что-то выбрасывают и что-то вставляют в своих творениях.
Но компетентные люди неспроста говорят что у них: "на хитрую '====' есть '+++' с винтом".
И несмотря на то, что не все их слова мне понятны, я все-таки им верю.

(для тех кто не понял - кое-где использованы слоганы из к/ф "Кин-Дза=Дза").

Текст/код самого программного примера выложу в следующем сообщении.
Куда выложить архив с утилитами и примерами?


Ссылки на разные полезные для использования и даже развития вещи:
1
2
3

4
5
6
7
8
Вот код программы-примера на чистом СИ.
Где строка #include "acax17enu.h" указывает на АутоКАД-2007
Её надо заменить на то, что соответствует версии установленной на каждой конкретной машине.
И разумеется в каталоге проекта должен быть также H-файл соответствующей версии.

Код
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define INITGUID
#include <ole2.h>

#define COBJMACROS
#define __AutoCAD_INCLUDEALIAS
typedef DWORD OLE_COLOR;
//line 3197: //typedef INT64 LONG_PTR ;   /// allready in basetsd.h
#include "acax17enu.h"   // from acax17enu.tlb
//#include "acad.h"

#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "oleaut32.lib")

char *szAppName = "AutoCadTest";

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
   HRESULT hr;
   CLSID clsid;
   IAcadApplication *pACad = NULL;
   IAcadDocument *pACadDoc = NULL;
   IAcadUtility* pUtility = NULL;
   IAcadDatabase *pDataBase = NULL;
   IAcadModelSpace *pModelSpace = NULL;

   CoInitialize(NULL);
   {
      hr = CLSIDFromProgID(L"AutoCAD.Application", &clsid);
      hr = CoCreateInstance(&clsid, NULL, CLSCTX_ALL, &IID_IAcadApplication, (void **)&pACad);
      if (pACad) {
         IAcadApplication_put_Visible(pACad, -1);
         IAcadApplication_get_ActiveDocument(pACad, &pACadDoc);
         if (pACadDoc) {
            IAcadDocument_get_Utility(pACadDoc, &pUtility);
            IAcadDocument_get_Database(pACadDoc, &pDataBase);
            if (pDataBase) {
               IAcadDatabase_get_ModelSpace(pDataBase, &pModelSpace);
               if (pModelSpace) {
                  IAcadLine *pLine;
                  VARIANT vpt1, vpt2;   // for points
                  SAFEARRAY sarray1, sarray2;   // for arrays
                  SAFEARRAYBOUND sab1[1], sab2[1];
                  sab1[0].lLbound = 0; sab1[0].cElements = 3;
                  sab2[0].lLbound = 0; sab2[0].cElements = 3;

                  VARIANT parm1[3], parm2[3];
                  parm1[0].vt = VT_R8; parm1[0].dblVal = 0.0;   // X
                  parm1[1].vt = VT_R8; parm1[1].dblVal = 0.0;   // Y
                  parm1[2].vt = VT_R8; parm1[2].dblVal = 0.0;   // Z
                  parm2[0].vt = VT_R8; parm2[0].dblVal = 100.0;   // X
                  parm2[1].vt = VT_R8; parm2[1].dblVal = 100.0;   // Y
                  parm2[2].vt = VT_R8; parm2[2].dblVal = 0.0;   // Z

                  sarray1.cDims = 1;
                  sarray1.fFeatures = FADF_VARIANT | FADF_HAVEVARTYPE | FADF_FIXEDSIZE | FADF_STATIC;
                  sarray1.cbElements = sizeof(VARIANT);
                  sarray1.cLocks = 0;
                  sarray1.pvData = &parm1;
                  sarray1.rgsabound[0].lLbound = 0;
                  sarray1.rgsabound[0].cElements = 3;

                  sarray2.cDims = 1;
                  sarray2.fFeatures = FADF_VARIANT | FADF_HAVEVARTYPE | FADF_FIXEDSIZE | FADF_STATIC;
                  sarray2.cbElements = sizeof(VARIANT);
                  sarray2.cLocks = 0;
                  sarray2.pvData = &parm2;
                  sarray2.rgsabound[0].lLbound = 0;
                  sarray2.rgsabound[0].cElements = 3;

                  IAcadUtility_CreateTypedArray(pUtility, &vpt1, VT_R8, &sarray1);
                  IAcadUtility_CreateTypedArray(pUtility, &vpt2, VT_R8, &sarray2);

                  IAcadModelSpace_AddLine(pModelSpace, vpt1, vpt2, &pLine);
                  if (pLine) {
                     MessageBox(0,"Line is drawn", szAppName, MB_OK);
                  } else MessageBox(0,"No Line",0,0);
               }
            }
         }
         IAcadApplication_Release(pACad);
      } else
         MessageBox(0, "No AutoCAD.Application ?", szAppName, MB_OK);
   }
   CoUninitialize();
   return 0;
}


Продублировал тему:
Геодезист.ру
и выложил там примеры.
Изменено: Сергей Климкин - 08-06-2016 00:29:03
Для практической работы программы нужно бы подключиться к уже загруженной копии АутоКАДа с реальным для работы чертежем.
Но я не нашел такой возможности.
Поэтому выбрал такой способ:
после загрузки программой новой копии АутоКАДа с пустым чертежом вызывается АутоКАД-овский диалог <FileOpen> и так далее.

Самое надежное для этого: команда AutoCAd'а через 'Command Prompt'
_open
выдает на экран диалог "Выбор файла" для загрузки чертежа.
Хорошо бы вставит еще и проверку не доступность этого:
Код
if (FILEDIA==0) // variable has value 'disable'
   FILEDIA=1; // set FileDialog to 'enable'

Из СИ-кода реально работает макрос (команда) IAcadDocument_ но только сдвоенная:
Код
   if (pACadDoc) {
      IAcadDocument_SendCommand(pACadDoc, L"_open ");
      IAcadDocument_SendCommand(pACadDoc, L"_OPEN");
   }

Макрос (команда) из 'IAcadUtility_Prompt' не работает никак:
Код
   /*
   if (pUtility) {
      IAcadUtility_Prompt(pUtility, L"_open ");
      IAcadUtility_Prompt(pUtility, L"_OPEN ");
      // variants of:
      //(pUtility)->lpVtbl->Prompt(pUtility, L"_open ");
      //IAcadUtility_Prompt(pUtility, L"_open \\n");
   }
   */

Вот первостепенные команды из файла acax17enu.tlb --> acax17enu.h:
EXTERN_C const IID IID_IAcadUtility;
------------------------------------
Код
//! A series of methods provided for utility purposes
typedef struct IAcadUtilityVtbl   /* Dual Interface */
В программе подключены Acad-макросы, поэтому вот так COBJMACROS:
IAcadUtility_Prompt(This, Message);
(This)->lpVtbl->Prompt(This, Message);
IAcadUtility_GetInput(This, pResult);
(This)->lpVtbl->GetInput(This, pResult);
IAcadUtility_GetKeyword(This, Prompt, pResult);
(This)->lpVtbl->GetKeyword(This, Prompt, pResult;
IAcadUtility_InitializeUserInput(This, Bits, KeyWordList);
(This)->lpVtbl->InitializeUserInput(This, Bits, KeyWordList);
IAcadUtility_CreateTypedArray(This, varArr, Type, inArgs);
(This)->lpVtbl->CreateTypedArray(This, varArr, Type, inArgs);
#define IAcadUtility_GetInteger(This, Prompt, pResult);
(This)->lpVtbl->GetInteger(This, Prompt, pResult);
IAcadUtility_GetReal(This, Prompt, pResult);
(This)->lpVtbl->GetReal(This, Prompt, pResult);
IAcadUtility_GetString(This, HasSpaces, Prompt, pResult);
(This)->lpVtbl->GetString(This, HasSpaces, Prompt, pResult);
IAcadUtility_GetPoint(This, Point, Prompt, pResult);
(This)->lpVtbl->GetPoint(This, Point, Prompt, pResult);
IAcadUtility_GetEntity(This, Object, PickedPoint, Prompt);
(This)->lpVtbl->GetEntity(This, Object, PickedPoint, Prompt);

А вот они же в "сыром" виде:
Код
/!< Posts a prompt to the command line
hr = Prompt(IAcadUtility *This, BSTR Message);
hr = GetInput(IAcadUtility *This, BSTR *pResult);
hr = GetKeyword(IAcadUtility *This, VARIANT Prompt, BSTR *pResult);
//!< Creates a variant that contains an array of typed arguments
hr = CreateTypedArray(IAcadUtility *This, VARIANT* varArr, INT Type, SAFEARRAY * inArgs);
//!< Gets a ... value from the user:
hr = GetInteger(IAcadUtility *This, VARIANT Prompt, INT *pResult);
hr = GetReal(IAcadUtility *This, VARIANT Prompt, DOUBLE *pResult);
hr = GetString(IAcadUtility *This, INT HasSpaces, VARIANT Prompt, BSTR *pResult);
hr = GetPoint(IAcadUtility *This, VARIANT Point, VARIANT Prompt, VARIANT *pResult);
//!< Gets an object interactively
hr = GetEntity(IAcadUtility *This, IDispatch** Object, VARIANT* PickedPoint, VARIANT Prompt);


Работающий пример выложу на сайте Геодезист.ру
Изменено: Сергей Климкин - 11-06-2016 22:06:55
Работающий пример.
Воспользоваться могут обладатели "реликта" 2007.
Программисты могут переписать для своих версий.

Программно рисует:
точки,
линии,
3д-полилинию,
окружность.
Программно получает:
координаты точки,
координаты (например конечной точки линии),
целое число (вводимое пользователем по запросу),
строку текста (также, но пока криво декодирует ее).

То есть основы программирования извне есть.

Исходник/СИ-проект и скриншот см. на сайте Геодезист.ру (Форум-->АутоДеск-->АутоКАД)
В архиве исходник и скомпилированный для версии 2007 пример.
По базовой линии (или двум точкам - например это некая строительная ось) и далее в цикле выбираемым парам точек (например это проектное положение свай и их фактическое положение после исполнительной съемки) создаются полилинии-стрелки и надписи вычисленных ординатных отклонений.
На выбор пользователя 7 размеров стрелок и 3 вида отображения величин отклонений.
Для обозначенной в начале темы задачи этого пожалуй достаточно.
И скриншот - как это может выглядеть.
См. Где лежит архив и скриншот
Программа 'Arrows' ('Стрелка' в девичестве).
Практическое применение программирования АутоКАД'а извне.
1.
Небольшое окно для пользовательских настроек:
выбор единиц чертежа (в некотором смысле его масштаб - 3 типа),
выбор размера стрелок (7 размеров),
выбор допустимых отклонений проект--факт (7 ступеней),
запись сессии в текстовый файл.
2.
Создание слоев (Arrows и OrtoDiff)
Задание цвета на отображение (всё зеленым, что выше допуска - красным).
Завершение сессии - дважды ESC.
Перед закрытием окна (кнопка EXIT) еще раз нажать кнопку LogFile (дописывает в файл время окончания работы).

Для СИ-программистов (если такие еще не перевелись):
В программе реализованы некоторые наиболее востребованные функции, описания которых для СМ-программирования похоже нигде нет.

Вот пожалуй и всё.

Архив программы с исходниками и скриншот на сайте Геодезист.ру
Acad_Surface (6) 22.08.2016

Штриховка ('Hatch') в AutoCAD для программирования вещь непростая.
В СИ (в отличии от AutoLISP и VBA) функция штриховки (AddHatch) не работает.
Вот как это прописано в acax17enu.h (acax17enu.tlb):
hr = AddHatch(IAcadModelSpace *This, INT PatternType, BSTR PatternName, VARIANT_BOOL Associativity, VARIANT HatchObjectType, IAcadHatch* *pResult);
или через макрос:
IAcadModelSpace_AddHatch(This, PatternType, PatternName, Associativity, HatchObjectType, pResult);

Аргумент 'VARIANT HatchObjectType' совсем не 'typedef enum acHatchObjectType'.
И если в AutoLISP и VBA этот агрумент необязательный (то есть может быть пропущен), то в СИ без него функция не работает.
Нигде кроме функции AddHatch аргумент 'HatchObjectType' не встречается.
И поэтому его "облик" в структуре VARIANT неизвестен.

Поэтому, после многих безуспешных проб (и ошибок), после чтения форумов на тему 'AutoCAD HATCH', пришлось воспользоваться функцией 'command' из AutoLISP.
При этом AutoLISP 'command' успешно штрихует 3dFace (если верить Help'у то это невозможно).
Также в командную строку можно сразу вставить различные свойства штриховки.
Одним словом старина AutoLISP выручил.

В приложенном архиве пример создания поверхности (Surface as TIN) из '3dFace' по нерегулярно расположенным точкам:
1. из точек существующих в текущем чертеже Drawing - кнопка 'SURFACE',
2. из точек X,Y,Z в файле 'points.txt' - кнопка 'Read File',

То же самое (1 и 2) можно выполнить используя внешнюю LISP программу (DTM.lsp + DTM.dsl) румынского программиста Nikolae Manda - кнопка 'DTM LISP'.
Эти два файла должны находиться в системной папке AutoCAD, например в C:\Program Files\AutoCAD 20xx\Support\

Радиокнопка 'Hatch 3dFaces' для выполнения штриховки всех построенных треугольников.
Это поможет увидеть пропущенные программой островки поверхности и заполнить их вручную.

Программный код, выполняющий триангуляцию:
vdefs.h
voron_main.c (main.c)
voronoi.c
geometry.c
heap.c
memory.c
edgelist.c
output.c
его автор Steve J. Fortune ( ©1987-1992 года).
Не смотря на возраст кода, потребовалось совсем немного исправлений для включения в данный проект.

Есть и другие свободные исходники, например Clarkson-Delaunay - Ken Clarkson © 1995

Почти все то, что есть в свободном доступе триангулирует так называемую 'выпуклую оболочку' ('Convex Hull').
Для серьезной работы требуется реализация "Триангуляция Делоне с условиями", и как все серьезное в открытом доступе отсутствует.

Это для общего развития:
http://algolist.manual.ru/maths/geom/
http://gts.sourceforge.net

См. Где лежит архив и скриншот
Проект скомпилирован для AutoCAD 2012

См. Где лежит архив
Изменено: Сергей Климкин - 23-08-2016 17:56:06
Два варианта программы acad_surface для АутоКАД'а 2010-2012:
1. треугольники строит ЛИСП-программа DTM.lsp+DTM.dcl (эти файлы надо положить в \AutoCAD xxxx\Support\)
2. треугольники строит библиотека acax18enu.tlb
Внутри архива есть FtypeLib.exe - это конвертер XXXX.TLB-->XXXX.H для проектов на голом СИ (не С++).
Там же DWG-файл пример на 399 точек (специально сделан в "постороннем КАДе" и сохранен как AC-2004).

Повторю специально для:
- тех кто в танке,
- тех кто "Ничего не понял",
- проповедников их иных "миров, сект и конфессий" -------->>>
это всего-лишь пример создания программы, которая:
- получает доступ к "объектной модели АутоКАД" не изнутри (как VBA, ObjectARX, AutoLISP), а извне,
- использует для своего функционирования только системные функции ОС Windows-32 и ОДНУ библиотеку АутоКАД acaxXXenu.tlb,
- скомпилированная в EXE-файл имеет размер 65 КилоБайт,
- может посылать команды в командную строку АутоКАДа, а может запускать на исполнение LISP-программы,
- читает из и пишет в внешние для АутоКАД файлы,
- работает с уже открытым чертежем, или создает новый чертеж АутоКАДа,
- показывает никак не документированные возможности программирования АутоКАДа ИЗ ЧИСТОГО/ГОЛОГО СИ.

То есть это для тех кто сам делает (или намеревается делать) программы.
Для просто пользователей АутоКАДа это игрушка, которая построит поверхность из треугольников '3dFace'.

Мое скромное мнение (""ИМХО""):
самый легкий инструмент для программирования в АутоКАД - VBA, который АутоДеск "кинул" вместе с VB-программистами,
а самый надежный инструмент для программирования в АутоКАД - AutoLISP - его никто "не обидит" - он там изначальный.

Мне пока нечего добавить, свой интерес я удовлетворил.
Спорить ни с кем не намерен - пустое это занятие.

Архив и скриншоты на форуме сайта geodesist.ru
Страницы: 1
Читают тему (гостей: 1, пользователей: 0, из них скрытых: 0)