Как используя 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 с тем же запросом.
Выглядеть это может приблизительно так:
- // "acax19enu.tlb"для AutoCAD 2013 и 2014, "acax20enu.tlb" для AutoCAD 2015
- #import "acax19enu.tlb" raw_interfaces_only no_namespace
- #define nRetry (100)
- #define mSleep (500)
- #define CallAcad(__x__) \
- { \
- HRESULT hr; \
- for (int i = 0; i < nRetry; i++) { \
- if (!FAILED(hr = (__x__))) break; \
- if (hr != RPC_E_CALL_REJECTED && \
- hr != RPC_E_RETRY && \
- hr != RPC_E_SERVERCALL_RETRYLATER) \
- break; \
- Sleep(mSleep); \
- } \
- _com_util::CheckError(hr); \
- }
- HRESULT PointToVariant(const double pt[3], VARIANT* pVal);
- IAcadState* pState = NULL;
- bool checkReady()
- {
- if (pState == NULL) return false;
- VARIANT_BOOL bReady = VARIANT_FALSE;
- for (int i = 0; i < nRetry; i++) {
- if (pState->get_IsQuiescent(&bReady) == S_OK && bReady == VARIANT_TRUE) break;
- Sleep(mSleep);
- }
- return (bReady == VARIANT_TRUE);
- }
- void CMFCAcadAppDlg::OnBnClickedStartacad()
- {
- // TODO: Add your control notification handler code here
- ::CoInitialize(NULL);
- COleMessageFilter* pFilter = AfxOleGetMessageFilter();
- if (pFilter != NULL) {
- DWORD dTimedelay = 1000;
- pFilter->SetMessagePendingDelay(dTimedelay); // Врем ожидания - 1 сек
- pFilter->SetRetryReply(dTimedelay);
- pFilter->EnableBusyDialog(FALSE); // Запрещаем диалог "сервер занят"
- pFilter->EnableNotRespondingDialog(FALSE); // Запрещаем диалог "сервер не отвечает"
- }
- try
- {
- CLSID clsid ;
- HRESULT hr = S_OK;
- // Для AutoCAD 2014 - L"AutoCAD.Application.19.1"
- // Для AutoCAD 2015 - L"AutoCAD.Application.20"
- // Если версия AutoCAD не важна, то можно L"AutoCAD.Application"
- _com_util::CheckError(CLSIDFromProgID (L"AutoCAD.Application.19.1", &clsid));
- IAcadApplicationPtr pApp;
- // Запускаем AutoCAD
- CallAcad(pApp.CreateInstance(clsid));
- pState = NULL; // Получаем состояние AutoCAD
- CallAcad(pApp->GetAcadState(&pState));
- // Делаем AutoCAD видимым, если в этом есть необходимость
- if (checkReady()) CallAcad(pApp->put_Visible(VARIANT_TRUE));
- // Получаем список открытых документов AutoCAD
- IAcadDocumentsPtr pDocs;
- if (checkReady()) CallAcad(pApp->get_Documents(&pDocs));
- IAcadDocumentPtr pDoc;
- // Создаем новый документ-чертеж (для простоты с шаблоном acadiso.dwt)
- if (checkReady()) CallAcad(pDocs->Add(_variant_t(_T("acadiso.dwt")), &pDoc));
- // Будем работать с пространством модели
- IAcadModelSpacePtr pSpace;
- if (checkReady()) CallAcad(pDoc->get_ModelSpace(&pSpace));
- // Добавим отрезок с началом в {0 0 0} и концом в {10 10 0}
- IAcadLinePtr pLine;
- _variant_t vStart, vEnd; // Начало и конец отрезка
- const int iMax = 10;
- double p1[3] = {0, 0, 0}, p2[3] = { iMax, iMax, 0};
- CallAcad(PointToVariant(p1, &vStart));
- CallAcad(PointToVariant(p2, &vEnd));
- if (checkReady()) {
- CallAcad(pSpace->AddLine(/*начало*/ vStart, /*конец*/ vEnd, &pLine));
- }
- if (checkReady()) {
- CallAcad(pLine->put_color(acYellow)); // Меняем цвет на желтый
- }
- // Добавим окружности вдоль отрезка с радиусом 1
- IAcadCirclePtr pCircle;
- for (int i = 0; i <= iMax; i++) {
- p1[0] = i; p1[1] = i;
- PointToVariant(p1, &vStart);
- if (checkReady()) {
- CallAcad(pSpace->AddCircle( /*центр*/ vStart, /*радиус*/ 1.0, &pCircle));
- }
- if (checkReady()) {
- CallAcad(pCircle->put_color(acGreen)); // Меняем цвет на зеленый
- }
- }
- if (checkReady()) CallAcad(pApp->ZoomExtents()); // Показываем границы
- if (checkReady()) CallAcad(pDoc->SaveAs(_bstr_t(_T("C:\\test.dwg"))));
- if (checkReady()) CallAcad(pApp->Quit());
- }
- catch(const _com_error e)
- {
- if (e.ErrorMessage()!=NULL) {
- CString msg; msg.Format(_T("Error: %s"),e.ErrorMessage());
- AfxMessageBox(msg,MB_ICONERROR);
- }
- }
- ::CoUninitialize();
- }
- //////////////////////////////////////////////////////////////////////////
- // Функция для преобразования точки double pt[3] в VARIANT
- //////////////////////////////////////////////////////////////////////////
- HRESULT PointToVariant(const double pt[3], VARIANT* pVal)
- {
- pVal->vt = VT_ARRAY | VT_R8;
- SAFEARRAYBOUND rgsaBound;
- rgsaBound.lLbound = 0L;
- rgsaBound.cElements = 3;
- pVal->parray = SafeArrayCreate(VT_R8, 1, &rgsaBound);
- if (!pVal->parray) return E_OUTOFMEMORY;
- HRESULT hr;
- for (long i = 0; i < 3; i++) {
- if ((hr = SafeArrayPutElement(pVal->parray,&i,(void*)&pt[i])) != S_OK) return hr;
- }
- return S_OK;
- }
Полный проект (для VS 2013)
Автор перевода: Александр Ривилис
Обсуждение: http://adn-cis.org/forum/index.php?topic=981
Опубликовано 25.09.2014