ADN Open CIS
Сообщество программистов Autodesk в СНГ

25/09/2014

Как используя Visual C++ запустить AutoCAD и заставить его выполнять некоторые действия.

AutoCAD представляет собой COM-сервер и для работы с ним имеется AutoCAD ActiveX/COM API. По этому API есть и документация, которая ориентирована на VBA и немного на VisualLisp (на английском языке), но которая позволяет понять структуру и возможности этого API.

AutoCAD 2015 – Developer's Guide и AutoCAD 2015 – Reference Guide

Кроме того документацию можно скачать полностью: http://images.autodesk.com/adsk/files/autocad_2013_activex_help.zip

Я сделал готовый пример с комментариями, который используя некоторые средства AutoCAD ActiveX/COM API, позволяет понять как можно сделать диалоговое окно с кнопкой, по которой будет запущен AutoCAD, будет создан новый документ, отрисованы некоторые примитивы, а затем этот документ будет сохранен в указанном месте. После этого работа AutoCAD будет завершена. Основная проблема при работе с AutoCAD через ActiveX API заключается в том, что в это время с AutoCAD может работать пользователь, или AutoCAD занимается своими делами и занят. В ряде случае он возвращает коды ошибки:

  • RPC_E_CALL_REJECTED
  • RPC_E_RETRY
  • RPC_E_SERVERCALL_RETRYLATER

Эти ошибки следует обработать и через некоторый промежуток времени повторно обратится к AutoCAD с тем же запросом.

Выглядеть это может приблизительно так:

Код - C++: [Выделить]
  1. // "acax19enu.tlb"для AutoCAD 2013 и 2014, "acax20enu.tlb" для AutoCAD 2015
  2. #import "acax19enu.tlb" raw_interfaces_only no_namespace
  3. #define nRetry (100)
  4. #define mSleep (500)
  5. #define CallAcad(__x__) \
  6. { \
  7.   HRESULT hr; \
  8.   for (int i = 0; i < nRetry; i++) { \
  9.      if (!FAILED(hr = (__x__))) break; \
  10.      if (hr != RPC_E_CALL_REJECTED &&  \
  11.          hr != RPC_E_RETRY && \
  12.          hr != RPC_E_SERVERCALL_RETRYLATER) \
  13.      break; \
  14.      Sleep(mSleep); \
  15.   } \
  16.   _com_util::CheckError(hr); \
  17. }
  18.  
  19. HRESULT PointToVariant(const double pt[3], VARIANT* pVal);
  20.  
  21. IAcadState* pState = NULL;
  22.  
  23. bool checkReady()
  24. {
  25.   if (pState == NULL) return false;
  26.   VARIANT_BOOL bReady = VARIANT_FALSE;
  27.   for (int i = 0; i < nRetry; i++)  {
  28.     if (pState->get_IsQuiescent(&bReady) == S_OK && bReady == VARIANT_TRUE) break;
  29.     Sleep(mSleep);
  30.   }
  31.   return (bReady == VARIANT_TRUE);
  32. }
  33.  
  34. void CMFCAcadAppDlg::OnBnClickedStartacad()
  35. {
  36.   // TODO: Add your control notification handler code here
  37.   ::CoInitialize(NULL);
  38.  
  39.   COleMessageFilter* pFilter = AfxOleGetMessageFilter();
  40.   if (pFilter != NULL) {
  41.     DWORD dTimedelay = 1000;
  42.     pFilter->SetMessagePendingDelay(dTimedelay); // Врем ожидания - 1 сек
  43.     pFilter->SetRetryReply(dTimedelay);
  44.     pFilter->EnableBusyDialog(FALSE); // Запрещаем диалог "сервер занят"
  45.     pFilter->EnableNotRespondingDialog(FALSE); // Запрещаем диалог "сервер не отвечает"
  46.   }
  47.   try
  48.   {
  49.     CLSID clsid ;
  50.     HRESULT hr = S_OK;
  51.     // Для AutoCAD 2014 - L"AutoCAD.Application.19.1"
  52.     // Для AutoCAD 2015 - L"AutoCAD.Application.20"
  53.     // Если версия AutoCAD не важна, то можно L"AutoCAD.Application"
  54.     _com_util::CheckError(CLSIDFromProgID (L"AutoCAD.Application.19.1", &clsid));
  55.     IAcadApplicationPtr pApp;
  56.     // Запускаем AutoCAD
  57.     CallAcad(pApp.CreateInstance(clsid));
  58.     pState = NULL; // Получаем состояние AutoCAD
  59.     CallAcad(pApp->GetAcadState(&pState));
  60.     // Делаем AutoCAD видимым, если в этом есть необходимость
  61.     if (checkReady()) CallAcad(pApp->put_Visible(VARIANT_TRUE));
  62.     // Получаем список открытых документов AutoCAD
  63.     IAcadDocumentsPtr pDocs;
  64.     if (checkReady()) CallAcad(pApp->get_Documents(&pDocs));
  65.     IAcadDocumentPtr pDoc;
  66.     // Создаем новый документ-чертеж (для простоты с шаблоном acadiso.dwt)
  67.     if (checkReady()) CallAcad(pDocs->Add(_variant_t(_T("acadiso.dwt")), &pDoc));
  68.     // Будем работать с пространством модели
  69.     IAcadModelSpacePtr pSpace;
  70.     if (checkReady()) CallAcad(pDoc->get_ModelSpace(&pSpace));
  71.     // Добавим отрезок с началом в {0 0 0} и концом в {10 10 0}
  72.     IAcadLinePtr pLine;
  73.     _variant_t vStart, vEnd; // Начало и конец отрезка
  74.     const int iMax = 10;
  75.     double p1[3] = {0, 0, 0}, p2[3] = { iMax, iMax, 0};
  76.     CallAcad(PointToVariant(p1, &vStart));
  77.     CallAcad(PointToVariant(p2, &vEnd));
  78.     if (checkReady()) {
  79.       CallAcad(pSpace->AddLine(/*начало*/ vStart, /*конец*/ vEnd, &pLine));
  80.     }
  81.     if (checkReady()) {
  82.       CallAcad(pLine->put_color(acYellow)); // Меняем цвет на желтый
  83.     }
  84.     // Добавим окружности вдоль отрезка с радиусом 1
  85.     IAcadCirclePtr pCircle;
  86.     for (int i = 0; i <= iMax; i++) {
  87.       p1[0] = i; p1[1] = i;
  88.       PointToVariant(p1, &vStart);
  89.       if (checkReady()) {
  90.         CallAcad(pSpace->AddCircle( /*центр*/ vStart, /*радиус*/ 1.0, &pCircle));
  91.       }
  92.       if (checkReady()) {
  93.         CallAcad(pCircle->put_color(acGreen)); // Меняем цвет на зеленый
  94.       }
  95.     }
  96.     if (checkReady()) CallAcad(pApp->ZoomExtents()); // Показываем границы
  97.     if (checkReady()) CallAcad(pDoc->SaveAs(_bstr_t(_T("C:\\test.dwg"))));
  98.     if (checkReady()) CallAcad(pApp->Quit());
  99.   }
  100.   catch(const _com_error e)
  101.   {
  102.     if (e.ErrorMessage()!=NULL) {
  103.       CString msg; msg.Format(_T("Error: %s"),e.ErrorMessage());
  104.       AfxMessageBox(msg,MB_ICONERROR);
  105.     }
  106.   }
  107.   ::CoUninitialize();
  108. }
  109.  
  110. //////////////////////////////////////////////////////////////////////////
  111. //  Функция для преобразования точки double pt[3] в VARIANT
  112. //////////////////////////////////////////////////////////////////////////
  113. HRESULT PointToVariant(const double pt[3], VARIANT* pVal)
  114. {
  115.   pVal->vt = VT_ARRAY | VT_R8;
  116.   SAFEARRAYBOUND rgsaBound;
  117.   rgsaBound.lLbound = 0L;
  118.   rgsaBound.cElements = 3;
  119.   pVal->parray = SafeArrayCreate(VT_R8, 1, &rgsaBound);
  120.   if (!pVal->parray)  return E_OUTOFMEMORY;
  121.   HRESULT hr;
  122.   for (long i = 0; i < 3; i++) {
  123.     if ((hr = SafeArrayPutElement(pVal->parray,&i,(void*)&pt[i])) != S_OK)  return hr;
  124.   }
  125.   return S_OK;
  126. }


Полный проект (для VS 2013)

Автор: Александр Ривилис
Автор перевода: Александр Ривилис

Обсуждение: http://adn-cis.org/forum/index.php?topic=981

Опубликовано 25.09.2014