| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Программирование, связанное с MS Office > VBA и DLL |
| Автор: Dementor 11.2.2016, 12:26 | ||
| И снова всем привет! Возникла проблема связывания макроса на VBA с DLL, которая пишется на C++. Сразу уточню - DLL пытаюсь писать так же я, поэтому нет никаких ограничений в решении проблемы - лишь бы решить. Итак в чем суть: есть макрос на VBA, который пишется под Microstation, и DLL, которая фактически реализует возможности http://www.cs.man.ac.uk/~toby/gpc/. В DLL пока есть одна функция, доступная для VBA, которая на входе получает два SAFEARRAY и один gpc_polygon, который в итоге, по моему мнению, она и должна заполнить. Как выглядит сама функция в DLL (ее внутренности не привожу, т.к. особого смысла в этом нет):
Из VBA в качестве Master и Slave подаются массивы Point3d (это тип данных, состоящий из 3-х double), которые сама DLL переводит в gpc_polygon, для того чтобы уже их отдать General Polygon Clipper для обработки. В итоге работы General Polygon Clipper мы получаем еще один gpc_polygon, который и должны как-то вернуть в макрос VBA. Вопрос именно в том как этот gpc_polygon вернуть. Я предполагал, что за это будет отвечать тот самый *Result, но к сожалению это не работает. Как я понимаю не работает по причине того, что сам gpc_polygon внутри себя содержит динамические массивы и соответственно VBA у меня на этом и падает, выдавая ошибку. При этом в том же gpc_polygon есть и обычная переменная num_contours, которая вполне нормально может быть возвращена через gpc_polygon *Result. Сам тип gpc_polygon я реализовал и на VBA конечно же, хотя если честно я не совсем уверен, что подобные вещи вообще нужно делать и что подобные типы данных могут передаваться. Очень прошу помощи! Как подобное можно реализовать? Может быть есть какой-то вариант возвращения не самого gpc_polygon, а просто нескольких массивов? |
| Автор: Akina 11.2.2016, 13:15 |
| Я мало что смыслю в С++, но назвал бы это процедурой, а не функцией. |
| Автор: Dementor 17.2.2016, 11:48 | ||
| Akina, я тоже не большой специалист, но все же это функция - она же возвращает значение, а процедура была бы void и ничего бы не возвращала. Но суть не в том как называть конечно. В общем решил я проблему использованием в качестве Result того же SAFEARRAY, который содержал Point3d. Но так как вернуться может несколько контуров, то пришлось пойти дальше и опять нарваться на непонимание. Я в DLL объявил вот такую структуру:
Итак на входе у функции имеются три SAFEARRAY, каждый из которых содержит внутри себя массив структур Contour. В общем если говорить по-простому, то на входе у нас есть куча контуров, каждый из которых состоит из кучи вершин. И в общем-то если говорить о Master и Slave, то все отлично - никаких проблем: передали, получили, обработали. А вот с заполнением Result есть проблема. Если, например, в первом варианте функции SAFEARRAY **Result у меня содержал просто массив Point3d, то все было отлично - функция его замечательно заполняла и далее VBA-макрос его замечательно обрабатывал. А вот теперь получается проблема: массив Result проходит изменение размера до нужного, далее в цикле по контурам, которые мне надо добавить в массив, я создаю еще один SAFEARRAY *NewContour, в который записываю вершины, и после того как вершины записаны я с помощью SafeArrayPutElement добавляю NewContour в Result. На мой взгляд все правильно и логично, но вот на деле оказывается иначе: после того как все отработает я получаю Result нужного размера, но содержащий в качестве всех элементов только последний добавленный NewContour. В общем видимо надо писать в раздел по C++, ибо я уже совсем не понимаю что не так. |
| Автор: Romikgy 17.2.2016, 12:08 |
| насколько мне известно понятие ссылка у с++ и у вба отличаются... поэтому могут вести по разному |
| Автор: Dementor 18.2.2016, 16:00 |
| В общем что бы проблема получила решение я опишу мое решение и может быть это кому-нибудь да пригодится (решение не является 100% правильным!). Итак, основная проблема у меня состояла в том, что я вынужден работать с SAFEARRAY, который содержит в качестве элементов другие SAFEARRAY, которые свою очередь содержат структуры Point3d (повторюсь, что Point3d - это просто три double). Самое правильное и легальное решение подобной проблемы (как я это понимаю) - объявить структуру в IDL и далее уже беспрепятственно создавать массивы таких структур (под массивами надо понимать именно SAFEARRAY, а не обычный массив C/C++). Но к сожалению я не владею полными данными о том, как это делать, поэтому был вынужден придумывать обход. Суть обхода заключается в том, что я должен был как-то "обмануть" систему. И вот как я это сделал: 1. На входе в DLL-функцию подается 3 массива SAFEARRAY, два из которых заполнены данными, а третий нужен для возврата результата в VBA; 2. Используя SafeArrayCopy мы копируем один из заполненных массивов в результирующий массив. Вот тут и была моя первая ошибка - я не копировал массив, а делал просто *Result = *Master; 3. Теперь результирующий массив у нас имеет как минимум один элемент (а это по условиям моей задачи тоже SAFEARRAY), который отвечает всем правилам (т.е. как-будто мы его создали SAFEARRAY используя структуру IDL). Почему как минимум один элемент я думаю понятно; 4. Изменяем размер результирующего массива с помощью SafeArrayRedim до нужного (при этом понимаем, что если у нас после копирования в результирующем массиве был только 1 элемент, а теперь стало 5, то первый элемент никак не изменился!); 5. А теперь копируем первый элемент результирующего массива во все остальные используя все тот же SafeArrayCopy. Таким образом мы получаем результирующий массив, состоящий из нужного количества элементов SAFEARRAY, которые гарантированно способны хранить в себе структуры Point3d. Думаю понятно, что тут была моя вторая ошибка ибо я делал вот такую ересь: NewContour = MasterArray[0].Vertexes;. Понятно, что такой подход нельзя назвать грамотным и правильным, но если нет желания копать про IDL, то вариант вполне рабочий. |
| Автор: kapbepucm 29.2.2016, 16:31 |
| насколько я правильно понял от пункта 2 до пункта 5 - это выполнение вне VBA, со есть в C++ DLL; а result получаете в VBA? корректный? VBA не даёт такого гибкого доступа к памяти, как это можно делать в C++, однако можно через winapi попробовать |