Возникла острая необходимость написать небольшое приложение под AutoCAD с использованием OLE Automation. Документация по всему этому есть только для VBA, а я пишу на С++. В один из методов (штриховка контура) требуется передавать массив объектов, которые составляют контур в AutoCAD. Как это сделать на VBA описано в Help. Однако на основе этой информации я не понял, как это сделать на С++. Метод, сгенерированный в качестве обертки из TypeLibrary требует массив объектов в виде tagVARIANT. Пробовал множество различных способов (формировал SAFEARRAY напрямую из указателей на интерфейсы контуров, из указателей IDispatch контуров и др.), все ведут либо к появлению ошибки: "Invalid object array", либо к Access Violation по адресу 00000000.
Здравствуйте, Аноним, Вы писали:
А>Возникла острая необходимость написать небольшое приложение под AutoCAD с использованием OLE Automation. Документация по всему этому есть только для VBA, а я пишу на С++. В один из методов (штриховка контура) требуется передавать массив объектов, которые составляют контур в AutoCAD. Как это сделать на VBA описано в Help. Однако на основе этой информации я не понял, как это сделать на С++. Метод, сгенерированный в качестве обертки из TypeLibrary требует массив объектов в виде tagVARIANT. Пробовал множество различных способов (формировал SAFEARRAY напрямую из указателей на интерфейсы контуров, из указателей IDispatch контуров и др.), все ведут либо к появлению ошибки: "Invalid object array", либо к Access Violation по адресу 00000000.
Покажи, как ты это далал. Непонятно, какая именно сигнатура тебе нужна
Re[2]: Как передать массив объектов в виде VARIANT
От:
Аноним
Дата:
01.04.04 16:39
Оценка:
Здравствуйте, Plutonia Experiment, Вы писали:
PE>Покажи, как ты это далал. Непонятно, какая именно сигнатура тебе нужна
Штриховка в AutoCAD создается последовательным вызовом двух методов:
методом добавления объекта Hatch и методом добавления контура к Hatch.
Вот пример на VBA из Help'а:
Sub Example_AppendOuterLoop()
' This example creates an associative hatch in model space.Dim hatchObj As AcadHatch
Dim patternName As String
Dim PatternType As Long
Dim bAssociativity As Boolean' Define the hatch
patternName = "ANSI31"
PatternType = 0
bAssociativity = True' Create the associative Hatch objectSet hatchObj = ThisDrawing.ModelSpace.AddHatch(PatternType, patternName, bAssociativity)
' Create the outer loop for the hatch.
' An arc and a line are used to create a closed loop.Dim outerLoop(0 To 1) As AcadEntity
Dim center(0 To 2) As Double
Dim radius As Double
Dim startAngle As Double
Dim endAngle As Double
center(0) = 5: center(1) = 3: center(2) = 0
radius = 1
startAngle = 0
endAngle = 3.141592
Set outerLoop(0) = ThisDrawing.ModelSpace.AddArc(center, radius, startAngle, endAngle)
Set outerLoop(1) = ThisDrawing.ModelSpace.AddLine(outerLoop(0).startPoint, outerLoop(0).endPoint)
' Append the outer loop to the hatch object, and display the hatch
hatchObj.AppendOuterLoop (outerLoop)
hatchObj.Evaluate
ZoomAll
End Sub
На C++ Builder обертка метода AppendOuterLoop выглядит так:
// [14] Append loops to the hatchvirtual HRESULT STDMETHODCALLTYPE AppendOuterLoop(VARIANT ObjectArray/*[in]*/) = 0;
Мне непонятно, как сформировать этот самый ObjectArray, чтобы он верно воспринимался.
Для начала я пытался передать массив объектов длиной 1, чтобы проверить, как это работает.
Делал это следующим образом:
HRESULT hr;
IAcadHatchPtr iHatch; //смарт-указатель, порожденный TComInterface
//создаю объект Hatch, все работает верно
hr = iModelSpace->AddHatch(acHatchPatternTypePreDefined,WideString("ANSI31"),0,&iHatch);
if (FAILED(hr)) return;
//пытаюсь сформировать массив
Variant VarArr[1];
VarArr[0] = (IAcadObject*)Contur; //Contur - объект типа IAcadPolylinePtr (смарт-указатель на полилинию)
//формирую массив SAFEARRAY размером 1 на основе массива Variant и передаю в метод добавления контура
//метод возвращает код ошибки, которому соответсвует сообщение "Invalid object Array"
hr = iHatch->AppendOuterLoop(VarArrayOf(VarArr,0));
if (FAILED(hr)) return; //выход по ошибке
Я пробовал формировать массив еще и другими способами, выполянл различные приведения типов и др.
Все попытки вели к одному из двух результатов, либо "Invalid object Array",
либо Access Violation at address 00000000 (или 00000001).
Мне поможет любая подсказка, даже без использования оберток C++ Builder. Главное, чтобы работало.
Re[3]: Как передать массив объектов в виде VARIANT
А> Dim outerLoop(0 To 1) As AcadEntity
А> Set outerLoop(0) = ThisDrawing.ModelSpace.AddArc(center, radius, startAngle, endAngle)
А> Set outerLoop(1) = ThisDrawing.ModelSpace.AddLine(outerLoop(0).startPoint, outerLoop(0).endPoint)
А> ' Append the outer loop to the hatch object, and display the hatch
А> hatchObj.AppendOuterLoop (outerLoop)
outerLoop — это SAFEARRAY(IDispatch*), а не SAFEARRAY(VARIANT), который передаешь ты.
Однако, скорее всего, одного SAFEARRAY(IDispatch*) будет недостаточно, так как обращение будет идти к vtable элементов массива, так что нужно использовать SafeArrayCreateEx с дополнительными фичами:
Здравствуйте, Vi2, Вы писали:
Vi2>Однако, скорее всего, одного SAFEARRAY(IDispatch*) будет недостаточно, так как обращение будет идти к vtable элементов массива, так что нужно использовать SafeArrayCreateEx с дополнительными фичами: Vi2>
Vi2>>Я не знаю, какая строчка будет правильной — с или без FADF_HAVEIID.
PE>FADF_HAVEIID нужно, если передаешь IID. Т.е. второй вариант
А вот и не угадал!
Я написал так, потому что в функции SafeArrayCreateEx есть описание
vt
The base type or the VARTYPE of each element of the array. The FADF_RECORD flag can be set for a variant type VT_RECORD, The FADF_HAVEIID flag can be set for VT_DISPATCH or VT_UNKNOWN, and FADF_HAVEVARTYPE can be set for all other VARTYPEs.
Но нужно было бы проверить, а проверка показывает, что верен первый вариант, т.е. функция сама достаточно умная, чтобы это понять. Тем более, что FADF_HAVEIID — это битовое поле, а VT_DISPATCH — отнюдь.
Здравствуйте, Vi2, Вы писали:
Vi2>Но нужно было бы проверить, а проверка показывает, что верен первый вариант, т.е. функция сама достаточно умная, чтобы это понять. Тем более, что FADF_HAVEIID — это битовое поле, а VT_DISPATCH — отнюдь.
Я думал, что FADF_HAVEIID заставляет вставить IID в структуру SAFEARRAY.
pvExtra
Points to the type information of the user-defined type, if you are creating a safearray of user-defined types. If the vt parameter is VT_RECORD, then pvExtra will be a pointer to an IRecordInfo describing the record. If the vt parameter is VT_DISPATCH or VT_UNKNOWN, then pvExtra will contain a pointer to a GUID representing the type of interface being passed to the array.
Здравствуйте, Аноним, Вы писали:
А>Возникла острая необходимость написать небольшое приложение под AutoCAD с использованием OLE Automation. Документация по всему этому есть только для VBA, а я пишу на С++. В один из методов (штриховка контура) требуется передавать массив объектов, которые составляют контур в AutoCAD. Как это сделать на VBA описано в Help. Однако на основе этой информации я не понял, как это сделать на С++. Метод, сгенерированный в качестве обертки из TypeLibrary требует массив объектов
Итак, скриптовый клиент SAX Basic
Sub Main
Dim obj1 As New Class1
Dim arr(2) As Class2
Set arr(0) = New Class2
Set arr(1) = New Class2
obj1.TestArray(arr)
End Sub
Т.е. для С++ клиента надо сделать точно такой же Сейфаррей и передать его по ссылке в вариант.
Re[2]: Как передать массив объектов в виде VARIANT
От:
Аноним
Дата:
02.04.04 13:29
Оценка:
Здравствуйте, Plutonia Experiment, Вы писали:
PE>В метод приходит вариант с VT = 0x6009.
PE> VT_DISPATCH = 9, PE> VT_ARRAY = 0x2000, PE> VT_BYREF = 0x4000,
PE>Посколько пришел VT_BYREF, то надо заглядывать не в array.parray, а array.pparray.
PE>array.pparray->fFeatures равняется 0x450
PE> FADF_DISPATCH 0x0400 PE> FADF_HAVEIID 0x0040 PE> FADF_FIXEDSIZE 0x0010
PE>Т.е. для С++ клиента надо сделать точно такой же Сейфаррей и передать его по ссылке в вариант.
Спасибо за советы, и помощь. Все теперь работает как надо.
Ты меня очень сильно выручил. Возможно, в процессе написания возникнут еще проблемы,
тогда обращусь к тебе, если ты не против.
А ты можешь подробнее описать, как проводил исследование чтобы узнать, какие биты передаются?
Может в будущем мне это поможет.
Для тех, у кого возникнут аналогичные проблемы, вот код, который я написал:
//создаю массив
SAFEARRAY* sa;
sa = SafeArrayCreateVectorEx(VT_DISPATCH,0,1,(void*)&IID_IDispatch);
//выставляю флаги
sa->fFeatures = FADF_DISPATCH | FADF_HAVEIID | FADF_FIXEDSIZE;
//получаю указатель на интерфейс IDispatch у смарт-указателя на IAcadPolyline
IDispatch* pIAcadEntity;
hr = Contur->QueryInterface(IID_IAcadEntity,(void **)&pIAcadEntity);
if (FAILED(hr)) return;
//добавляю в единственный элемент массива полученный указатель интерфейсlong idx[1] = {0};
hr = SafeArrayPutElement(sa,idx,pIAcadEntity);
if (FAILED(hr)) return;
//создаю Variant, в который надо обернуть SAFEARRAY для передачи в метод AppendOuterLoop
VARIANT ConturArray;
VariantInit(&ConturArray);
ConturArray.vt = VT_ARRAY|VT_DISPATCH|VT_BYREF;
ConturArray.pparray = &sa;
//вызываю проблемный метод, который теперь отлично работает
hr = iHatch->AppendOuterLoop(ConturArray);
if (FAILED(hr)) return;
//зачистка структур в памяти, дело до которой не доходит
pIAcadEntity->Release();
SafeArrayDestroy(sa);
Re[3]: Как передать массив объектов в виде VARIANT
Здравствуйте, Аноним, Вы писали:
А>Ты меня очень сильно выручил. Возможно, в процессе написания возникнут еще проблемы, А>тогда обращусь к тебе, если ты не против.
Валяй
А>А ты можешь подробнее описать, как проводил исследование чтобы узнать, какие биты передаются? А>Может в будущем мне это поможет.
Создал проект на ATL. Добавил два простых объекта. В одном из объектов заимплементил метод с параметром вариантом. Поставил брякпоинт.
Запустил IDE Sax Basic. Подключил созданную DLL.
На бейсике написал тот код,что приводил раньше.
Запустил, вывалился в дебаг и постомтрел, что где и как передается.
Потом написал ответ сюда.
Re[3]: Как передать массив объектов в виде VARIANT
А> //создаю Variant, в который надо обернуть SAFEARRAY для передачи в метод AppendOuterLoop
А> VARIANT ConturArray;
А> VariantInit(&ConturArray);
А> ConturArray.vt = VT_ARRAY|VT_DISPATCH|VT_BYREF;
А> ConturArray.pparray = &sa;
А зачем использовать VT_BYREF? Чтобы поиметь проблемы? Ну и что что SAX Basic передает его как ByRef, он ведь за памятью следит автоматически, не то что С++.
Re[4]: Как передать массив объектов в виде VARIANT
От:
Аноним
Дата:
03.04.04 04:42
Оценка:
Здравствуйте, Vi2, Вы писали:
Vi2>
А>> //создаю Variant, в который надо обернуть SAFEARRAY для передачи в метод AppendOuterLoop
А>> VARIANT ConturArray;
А>> VariantInit(&ConturArray);
А>> ConturArray.vt = VT_ARRAY|VT_DISPATCH|VT_BYREF;
А>> ConturArray.pparray = &sa;
Vi2>А зачем использовать VT_BYREF? Чтобы поиметь проблемы? Ну и что что SAX Basic передает его как ByRef, он ведь за памятью следит автоматически, не то что С++. Vi2>
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, Plutonia Experiment, Вы писали:
PE>>В метод приходит вариант с VT = 0x6009.
PE>> VT_DISPATCH = 9, PE>> VT_ARRAY = 0x2000, PE>> VT_BYREF = 0x4000,
PE>>Посколько пришел VT_BYREF, то надо заглядывать не в array.parray, а array.pparray.
PE>>array.pparray->fFeatures равняется 0x450
PE>> FADF_DISPATCH 0x0400 PE>> FADF_HAVEIID 0x0040 PE>> FADF_FIXEDSIZE 0x0010
PE>>Т.е. для С++ клиента надо сделать точно такой же Сейфаррей и передать его по ссылке в вариант.
А>Спасибо за советы, и помощь. Все теперь работает как надо. А>Ты меня очень сильно выручил. Возможно, в процессе написания возникнут еще проблемы, А>тогда обращусь к тебе, если ты не против.
А>А ты можешь подробнее описать, как проводил исследование чтобы узнать, какие биты передаются? А>Может в будущем мне это поможет.
А>Для тех, у кого возникнут аналогичные проблемы, вот код, который я написал:
А>
А> //создаю массив
А> SAFEARRAY* sa;
А> sa = SafeArrayCreateVectorEx(VT_DISPATCH,0,1,(void*)&IID_IDispatch);
А> //выставляю флаги
sa->>fFeatures = FADF_DISPATCH | FADF_HAVEIID | FADF_FIXEDSIZE;
А> //получаю указатель на интерфейс IDispatch у смарт-указателя на IAcadPolyline
А> IDispatch* pIAcadEntity;
А> hr = Contur->QueryInterface(IID_IAcadEntity,(void **)&pIAcadEntity);
А> if (FAILED(hr)) return;
А> //добавляю в единственный элемент массива полученный указатель интерфейс
А> long idx[1] = {0};
А> hr = SafeArrayPutElement(sa,idx,pIAcadEntity);
А> if (FAILED(hr)) return;
А> //создаю Variant, в который надо обернуть SAFEARRAY для передачи в метод AppendOuterLoop
А> VARIANT ConturArray;
А> VariantInit(&ConturArray);
А> ConturArray.vt = VT_ARRAY|VT_DISPATCH|VT_BYREF;
А> ConturArray.pparray = &sa;
А> //вызываю проблемный метод, который теперь отлично работает
А> hr = iHatch->AppendOuterLoop(ConturArray);
А> if (FAILED(hr)) return;
А> //зачистка структур в памяти, дело до которой не доходит
А> pIAcadEntity->Release();
А> SafeArrayDestroy(sa);
А>
Я пытался сделать как написано в этом сообщении, но при компиляции получаю сообщение
error C2065: 'IID_IAcadEntityPtr' : undeclared identifier
Где взять IID_IAcadEntity
Я только начинаю изучать COM, OLE
поэтому наверное это неправильнй вопрос
помогите пожалуйста
заранее благодарен
Здравствуйте, andreyVC, Вы писали:
VC>Я пытался сделать как написано в этом сообщении, но при компиляции получаю сообщение VC>error C2065: 'IID_IAcadEntityPtr' : undeclared identifier VC>Где взять IID_IAcadEntity
Для того, чтобы ты мог использовать AutoCAD в качестве Automation сервера, следует предварительно сымпортировать его библиотеку типов в твой проект. Один из наиболее простых путей — это использование директивы #import (можешь почитать статью "Директива #import"
). В качестве filename можешь использовать например ProgID ("progid:AutoCAD.Application") или путь к главному исполняемому файлу AutoCAD-a (обычно библиотеки типов лежат в них).
It is always bad to give advices, but you will be never forgiven for a good one.
Oscar Wilde
E>Для того, чтобы ты мог использовать AutoCAD в качестве Automation сервера, следует предварительно сымпортировать его библиотеку типов в твой проект. Один из наиболее простых путей — это использование директивы #import (можешь почитать статью "Директива #import"
). В качестве filename можешь использовать например ProgID ("progid:AutoCAD.Application") или путь к главному исполняемому файлу AutoCAD-a (обычно библиотеки типов лежат в них).
я сделал импорт acad.tlb — у меня AutoCAD2000
Вот мой код:
void CTestACADDlg::OnACAD()
{
using namespace AutoCAD;
::CoInitialize(NULL);
try
{
CLSID clsid ;
_com_util::CheckError(CLSIDFromProgID (L"AutoCAD.Application", &clsid));
IAcadApplicationPtr pApp;
Здравствуйте, ekamaloff, Вы писали:
E>Здравствуйте, andreyVC, Вы писали:
VC>>Я пытался сделать как написано в этом сообщении, но при компиляции получаю сообщение VC>>error C2065: 'IID_IAcadEntityPtr' : undeclared identifier VC>>Где взять IID_IAcadEntity
E>Для того, чтобы ты мог использовать AutoCAD в качестве Automation сервера, следует предварительно сымпортировать его библиотеку типов в твой проект. Один из наиболее простых путей — это использование директивы #import (можешь почитать статью "Директива #import"
). В качестве filename можешь использовать например ProgID ("progid:AutoCAD.Application") или путь к главному исполняемому файлу AutoCAD-a (обычно библиотеки типов лежат в них).
Прочитал статью про директиву импорт.
Но в ней нет необходимой информации.
В самом начале есть ссылка на MSDN, где и нашелся ответ.
Это аттрибут named_guids:
The named_guids attribute
The named_guids attribute tells the compiler to define and initialize GUID variables in old style, of the form LIBID_MyLib, CLSID_MyCoClass, IID_MyInterface, and DIID_MyDispInterface.
Как написано в MSDN – это старый стиль.
А какой новый стиль (то есть используемый в настоящее время)?
Если можно, приведите, пожайлуста, какой-нибудь пример получения IID и работы с GUID-ами в новом стиле.
P.S. У кого-нибудь есть ссылка на электронный вариант Inside OLE или что-нибудь про COM. Наверное я не умею правильно задавать вопросы Гуглу: выдаёт сплошную рекламу дисков с электронными книгами (прости меня, Гугл)
Здравствуйте, andreyVC, Вы писали:
VC>Как написано в MSDN – это старый стиль. VC>А какой новый стиль (то есть используемый в настоящее время)? VC>Если можно, приведите, пожайлуста, какой-нибудь пример получения IID и работы с GUID-ами в новом стиле.
Видимо под новым стилем имеется ввиду (на примере MSHTML):
вместо MSHTML::CLSID_HTMLDocument
__uuidof(MSHTML::HTMLDocument)
вместо MSHTML::IID_IHTMLDocument2
__uuidof(MSHTML::IHTMLDocument2)
вместо MSHTML::DIID_DispHTMLDocument
__uuidof(MSHTML::DispHTMLDocument)
вместо MSHTML::LIBID_MSHTML
__uuidof(MSHTML::__MSHTML)
It is always bad to give advices, but you will be never forgiven for a good one.
Oscar Wilde