Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > SAFEARRAY внутри SAFEARRAY


Автор: Dementor 17.2.2016, 13:15
Доброго всем дня!

Вопрос переехал из темы: http://forum.vingrad.ru/forum/topic-386588.html

Что имеем:
1. Имеется структура:
Код

typedef struct
{
    SAFEARRAY *Vertexes;
} Contour;

2. Имеется функция:
Код

int __stdcall GPC::ClipPolygons(SAFEARRAY **Master, SAFEARRAY **Slave, SAFEARRAY **Result, int GPC_OPERATION)


Массивы Master и Slave заполняются на стороне VBA и содержат внутри себя те же структуры Contour. С ними проблем нет - они адекватно получаются в DLL и адекватно обрабатываются.

Проблема возникает в заполнении массива Result.

Вот код, который используется для его заполнения:
Код

        if (ResultPolygon.num_contours > 0)
        {
            *Result = *Master;
            
            // Изменяем размер массива Result до нужного            
            SAFEARRAYBOUND ResultBound;
            ResultBound.cElements = ResultPolygon.num_contours;
            ResultBound.lLbound = 0;
            hr = SafeArrayRedim(*Result, &ResultBound);
        
            if (Result != NULL)
            {
                for (LONG i = 0; i != ResultPolygon.num_contours; i++)
                {                    
                    SAFEARRAY *NewContour;
                    SAFEARRAYBOUND ContourBound;
                    
                    ContourBound.cElements = ResultPolygon.contour[i].num_vertices;
                    ContourBound.lLbound = 0;
                                                                                
                    NewContour = MasterArray[0].Vertexes;
                    hr = SafeArrayRedim(NewContour, &ContourBound);

                    for (LONG j = 0; j != ResultPolygon.contour[i].num_vertices; j++)
                    {
                        Point3d ResultPoint;
                        ResultPoint.x = ResultPolygon.contour[i].vertex[j].x;
                        ResultPoint.y = ResultPolygon.contour[i].vertex[j].y;
                        ResultPoint.z = 0;

                        hr = SafeArrayPutElement(NewContour, &j, &ResultPoint);
                    }

                    hr = SafeArrayPutElement(*Result, &i, &NewContour);
                }
                return ResultPolygon.num_contours;
            }
        }


Поясню для чего я делаю *Result = *Master и NewContour = MasterArray[0].Vertexes: если я все правильно понял из гугления, то для того, что бы легально создать SAFEARRAY, который содержит внутри себя структуру, надо эту самую структуру создавать через IDL, но если честно, то как именно это сделать я не очень понял и поэтому методом проб и ошибок пришел к выводу, что вот подобное работает (в предыдущей версии функции все три массива содержали просто структуры Point3d и Result формировался без каких-либо проблем).

А теперь в чем же проблема: после всех операций массив Result содержит нужное количество элементов, однако все элементы одинаковые и равны последнему, который в него передавался.

Я пытался вместо SAFEARRAY *NewContour задавать массив Contour с количеством элементов как в Result, заполнять вершинами каждый i-ый элемент и в Result записывать именно i-ый элемент. Но ничего не изменилось.

Я конечно понимаю, что я чего-то недопонимаю в самом C++ и SAFEARRAY в частности, но очень прошу помощи в том, что бы разобраться в возникшей проблеме.

Заранее всем откликнувшимся огромное спасибо!

Автор: xvr 17.2.2016, 15:48
Цитата(Dementor @  17.2.2016,  13:15 Найти цитируемый пост)
Поясню для чего я делаю *Result = *Master

Этого делать нельзя. ActiveX подсистема имеет полное право убить Master при возврате из вашего метода и ваш Result повиснет в воздухе.

Затем вы пытаетесь записать в SAFEARRAY *Vertexes структуру Point3d. Vertexes на это точно расчитан?

Автор: Dementor 17.2.2016, 16:11
xvr, хорошо, тогда можем предположить, что никакого Result нет, а результат работы функции я буду записывать в Master, меняя его размер с помощью SafeArrayRedim под нужный мне и далее уже заново заполняя его элементы. Такой подход будет правильным?

Что касается SAFEARRAY *Vertexes и Point3d. Т.к. все три массива заведомо создаются на стороне VBA, то там это выглядит так:

Код

Type Contour
    Vertexes() As Point3d
End Type

Function GPC_IntersectionPolygon(MasterElement As Element, SlaveElement As Element) As Collection
Dim t As Long
Dim MasterArray() As Contour, SlaveArray() As Contour, ResultArray() As Contour
' Тут есть промежуточная функция, которая заполняет Master и Slave, преобразовывая MasterElement и SlaveElement в массивы
t = ClipPolygons(MasterArray, SlaveArray, ResultArray, 1)
End Function


Т.е. с моей точки зрения все достаточно правильно. По крайней мере я вижу подтверждение правильности на примере массивов Master и Slave, элементы которых и представляют из себя массивы Point3d.

Что касается самой структуры Point3d, то она и в C++ и в VBA состоит из 3-х Double.

Автор: Dementor 17.2.2016, 17:11
Просто ради интереса переделал. Убрал Result, вместо него теперь Master (хотя может это тоже не верно). Ну и перешел с SafeArrayPutElement на работу через SafeArrayAccessData. Но в общем-то ничего и не изменилось в плане результата: теперь уже в Master образуется нужное количество элементов, но каждый элемент равен последнему записанному.

Код

        if (ResultPolygon.num_contours > 0)
        {                
            SAFEARRAYBOUND ResultBound;
            ResultBound.cElements = ResultPolygon.num_contours;
            ResultBound.lLbound = 0;

            hr = SafeArrayRedim(*Master, &ResultBound);
            if (FAILED(hr)) return -1;

            Contour HUGEP *pcnt;
            hr = SafeArrayAccessData(*Master, (void HUGEP**)&pcnt);
            if (FAILED(hr)) return -2;

            for (LONG i = 0; i != ResultPolygon.num_contours; i++)
            {                    
                SAFEARRAYBOUND ContourBound;                    
                ContourBound.cElements = ResultPolygon.contour[i].num_vertices;
                ContourBound.lLbound = 0;
                                                                                
                pcnt[i].Vertexes = pcnt[0].Vertexes; //Если этого не сделать, то SafeArrayRedim не выполняется и возвращается -3
                hr = SafeArrayRedim(pcnt[i].Vertexes, &ContourBound);
                if (FAILED(hr)) return -3;

                Point3d HUGEP *pvrt;
                hr = SafeArrayAccessData(pcnt[i].Vertexes, (void HUGEP**)&pvrt);
                if (FAILED(hr)) return -4;

                for (LONG j = 0; j != ResultPolygon.contour[i].num_vertices; j++)
                {
                    pvrt[j].x = ResultPolygon.contour[i].vertex[j].x;
                    pvrt[j].y = ResultPolygon.contour[i].vertex[j].y;
                    pvrt[j].z = 0;
                }

                SafeArrayUnaccessData(pcnt[i].Vertexes);
            }

            SafeArrayUnaccessData(*Master);
            return ResultPolygon.num_contours;

        }

Автор: Dementor 17.2.2016, 17:42
xvr, в общем все заработало.

Просто после SafeArrayRedim для главного SAFEARRAY я провел копирование нулевого элемента (который гарантированно всегда есть и который не уничтожается при SafeArrayRedim) во все остальные. Соответственно после этого адекватно проходит SafeArrayRedim для вложенного массива! Может это и костыль и так делать не очень правильно, но по крайней мере работает.

У меня остался один вопрос, который я не могу понять. Вот вы пишете, что "ActiveX подсистема имеет полное право убить Master при возврате из вашего метода и ваш Result повиснет в воздухе.", но почему ActiveX убьет массив, который после выполнения неких операций в DLL может еще использоваться в самом VBA?

Автор: xvr 17.2.2016, 18:00
Цитата(Dementor @  17.2.2016,  17:42 Найти цитируемый пост)
Просто после SafeArrayRedim для главного SAFEARRAY я провел копирование нулевого элемента (который гарантированно всегда есть и который не уничтожается при SafeArrayRedim) во все остальные. 

Это неправильно. Вы отдублировали ВСЕ массивы в один. Т.е. у вас реально один массив, по всем индексам.

Цитата(Dementor @  17.2.2016,  17:42 Найти цитируемый пост)
но почему ActiveX убьет массив, который после выполнения неких операций в DLL может еще использоваться в самом VBA? 

А может и нет. Поэтому может и убить.

Автор: Dementor 17.2.2016, 18:23
xvr, простите, что достаю вопросами, но сам понять это я не в состоянии и поэтому прошу пояснить.

Предположим, что вот это делать нельзя:
Код

Contour HUGEP *pcnt;
hr = SafeArrayAccessData(*Master, (void HUGEP**)&pcnt);
if (FAILED(hr)) return -2;
for (LONG i = 1; i != ResultPolygon.num_contours; i++)
   hr = SafeArrayCopy(pcnt[0].Vertexes, &pcnt[i].Vertexes);


Но без этого не выполняется вот это:
Код

SAFEARRAYBOUND ContourBound;                    
ContourBound.cElements = ResultPolygon.contour[i].num_vertices;
ContourBound.lLbound = 0;
hr = SafeArrayRedim(pcnt[i].Vertexes, &ContourBound);


И тогда возникает вопрос: если так делать нельзя, то как тогда можно сделать, что бы был корректный результат? Сейчас по крайней мере у меня в VBA после выполнения в функции DLL в массиве Master содержатся все необходимые элементы именно в том виде, в котором они и должны быть...

P.S. равно также работает если делать SafeArrayCopy(*Master, Result); и потом уже работать с Result. Поэтому даже вдвойне непонятно - почему это неправильно?

Автор: xvr 18.2.2016, 14:15
Цитата(Dementor @  17.2.2016,  18:23 Найти цитируемый пост)
Предположим, что вот это делать нельзя:

Вот как раз так делать можно: SafeArrayCopy(pcnt[0].Vertexes, &pcnt[i].Vertexes). А вот так pcnt[i].Vertexes=pcnt[0].Vertexes - нельзя

Цитата(Dementor @  17.2.2016,  18:23 Найти цитируемый пост)
P.S. равно также работает если делать SafeArrayCopy(*Master, Result); и потом уже работать с Result. Поэтому даже вдвойне непонятно - почему это неправильно?

Это правильно. А *Result=*Master - нет

Автор: Dementor 18.2.2016, 15:15
xvr, спасибо большое за пояснение, а то я думал, что и с таким подходом я не прав. Просто я конечно должен был уточнить, что я имел ввиду под копированием и тогда не было бы недопонимания. Еще раз большое спасибо за помощь и объяснения!  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)