Но проблема в том, что в случае применения технологии Native API возникает довольно нетривиальная задача обмена массивами информации между внешней компонентой и 1С: Предприятием. Как правильно было замечено в комментах к статье, эту задачу приходится решать либо многократным вызовов процедур, либо сериализацией содержимого массива.
Но если применять COM-технологию, то все в значительной степени упрощается. Дело в том, что в 1С есть такой малоизвестный, но в данном случае незаменимый тип данных, как COMSafeArray.
Описание:
Объектная оболочка над многомерным массивом SAFEARRAY из COM. Позволяет создавать и использовать SAFEARRAY для обмена данными между COM-объектами.
Для передачи массива в качестве параметра метода COM-объекта необходимо построить COMSafeArray нужной размерности с нужным типом элемента и указать построенный COMSafeArray в качестве значения входного параметра. Другие объекты 1С: Предприятия можно использовать в качестве значений входных параметров типа Массив только при наличии исчерпывающей информации о типах параметров в библиотеке типа COM-объекта.
Результат метода COM-объекта или значение выходного параметра типа Массив всегда представляется объектом COMSafeArray.
Помимо предоставления самого типа COMSafeArray 1С сопровождает его исчерпывающим набором методов, позволяющим преобразовывать стандартные массивы 1С в значения этого типа и обратно.
Весь фокус состоит в том, что если передать этот объект в качестве параметра в функцию внешней компоненты, то в самой внешней компоненте, построенной по технологии COM, этот массив будет получен в виде указателя на массив типа SAFEARRAY.
Аналогично, если вернуть указатель на такой массив из функции внешней компоненты, в 1С результат будет интерпретирован как объект типа COMSafeArray.
Мало того, в функции внешней компоненты можно изменять сам входной массив, указатель на который получен в качестве параметра, а из функции вернуть только S_OK. В 1С после этого можно продолжить работу с переданным массивом и он будет содержать в себе изменения сформированные внешней компонентой. Т.е. в связке между 1С и COM-компонентой можно использовать обычную передачу параметров по ссылке.
Покажем собственным примером.
В 1С все может быть размещено в простом событии кнопки:
Процедура КнопкаНажатие(Элемент) // Создание массива 1С. Массив = Новый Массив; Массив.Добавить(10.1); Массив.Добавить(20.2); Массив.Добавить(30.3); // Создание объекта COMSafeArray, содержащего значения типа double // на основании массива 1С. МассивПараметровКОМ = Новый COMSafeArray(Массив, "VT_R8"); // Вызов функции внешней компоненты. МассивРезультатовКОМ = Компонента2.ФункцияМассив(МассивПараметровКОМ); // Создание массивов 1С на основании объектов COMSafeArray. МассивПараметров = МассивПараметровКОМ.Выгрузить(); МассивРезультатов = МассивРезультатовКОМ.Выгрузить(); КонецПроцедуры
Т.е. создали и заполнили обычный массив, создали на его основе объект типа COMSafeArray и скормили его функции внешней компоненты.
Функция как-то преобразовывает входной COMSafeArray и что-то возвращает тоже в виде COMSafeArray.
Далее оба полученных объекта COMSafeArray мы выгружаем в обычные массивы 1С и просмотрщиком (Alt+F9) смотрим результат.
На стороне внешней компоненты всё выглядит ненамного сложнее. В соответствующем блоке switch-case, расположенном в функции CallAsFunc мы будем не только создавать результирующий массив, но и изменять сам входной массив.
Собственно, все описано в комментах:
case arrayFunc: { // ********************************** // *** РАБОТА С ИСХОДНЫМ МАССИВОМ *** // ********************************** // Получение указателя на нулевой элемент массива SAFEARRAY // (содержит первый параметр функции 1С, который тоже является массивом). long inputIdx = 0; void* inputVoidPtr = NULL; HRESULT hr = SafeArrayPtrOfIndex(*paParams, &inputIdx, &inputVoidPtr); // Приведение полученного указателя void* к типу VARIANT* // (т.к. мы точно знаем, что там именно VARIANT). VARIANT* inputVarPtr = (VARIANT*)inputVoidPtr; ///////////// ОПРЕДЕЛЕНИЕ ГРАНИЦ МАССИВА. long iLowerBound; hr = SafeArrayGetLBound(inputVarPtr->parray, 1, &iLowerBound); if(FAILED(hr)) return S_FALSE; long iUpperBound; hr = SafeArrayGetUBound(inputVarPtr->parray, 1, &iUpperBound); if(FAILED(hr)) return S_FALSE; ////////////////////////////////////////////// void* sourcePtr = NULL; // Указатель на элемент, заполняемый функцией SafeArrayPtrOfIndex. double* sourceValPtr = NULL; // Перебор входного массива от нижней границы к верхней. for(long l = iLowerBound; l <= iUpperBound; l++) { // Заполнение указателя на элемент. hr = SafeArrayPtrOfIndex(inputVarPtr->parray, &l, &sourcePtr); // Приведение указателей void* к требуемому типу. sourceValPtr = (double*)sourcePtr; // Изменение (инкремент) значения во входном массиве. ++(*sourceValPtr); } ///////////// СОЗДАНИЕ НОВОГО МАССИВА (С ТАКОЙ ЖЕ РАЗМЕРНОСТЬЮ). // Границы нового массива SAFEARRAYBOUND sBound[1]; sBound[0].cElements = iUBound - iLBound + 1; sBound[0].lLbound = 0; // Получение типа, хранящегося в исходном массиве. VARTYPE varType; hr = SafeArrayGetVartype(sArr, &varType); if(FAILED(hr)) return S_FALSE; // Создание нового массива. SAFEARRAY* sArrNew = SafeArrayCreate(varType, 1, sBound); ////////////////////////////////////////////// /////////// ПЕРЕБОР МАССИВА. // Указатели на элементы, заполняемые функцией SafeArrayPtrOfIndex. void* sourPtr = NULL; void* destPtr = NULL; // Перебор исходного массива от нижней границы к верхней. for(long l = iLBound; l <= iUBound; l++) { // Заполнение указателя на элемент. hr = SafeArrayPtrOfIndex(sArr, &l, &sourPtr); hr = SafeArrayPtrOfIndex(sArrNew, &l, &destPtr); // Приведение указателей void* к требуемому типу // и присвоение значений целевому массиву (исходный, умноженный на 2). *((double*)destPtr) = *((double*)sourPtr) * 2; } //////////////////////////////////////////// ///////////// ПРИСВОЕНИЕ ВОЗВРАЩАЕМОМУ ЗНАЧЕНИЮ ЗНАЧЕНИЯ МАССИВА. V_VT(pvarRetValue) = VT_ARRAY; V_ARRAY(pvarRetValue) = sArrNew; break; }
Т.е. мы получили исходный массив и как-то изменили (в данном случае — инкрементировали) его.
После этого создали новый массив такой же размерности и заполнили его некими значениями (в данном случае — входные, умноженные на 2).
Таким образом, если до обработки функцией внешней компоненты мы имели массив:
МассивПараметров: { 10.1, 20.2, 30.3 }
То после отрабатывания этой функции, полученные в 1С массивы примут следующий вид:
МассивПараметров: { 11.1, 21.2, 31.3 }
МассивРезультатов: { 22.2, 42.4, 62.6 }
Т.е. и массив-параметр и результирующий массив были обработаны функцией внешней компоненты и получены в 1С
в измененном виде.
Полный проект внешней компоненты (с расширенным набором демонстрационных функций) находится здесь.
ссылка на оригинал статьи http://habrahabr.ru/post/216381/
Добавить комментарий