Обмен массивами данных с внешними компонентами 1С

от автора

Как известно, подходы к созданию внешних компонент 1С подразумевают использование двух технологий — Native API и COM. Пример с Native API неплохо раскрыт в статье.

Но проблема в том, что в случае применения технологии Native API возникает довольно нетривиальная задача обмена массивами информации между внешней компонентой и 1С: Предприятием. Как правильно было замечено в комментах к статье, эту задачу приходится решать либо многократным вызовов процедур, либо сериализацией содержимого массива.

Но если применять COM-технологию, то все в значительной степени упрощается. Дело в том, что в 1С есть такой малоизвестный, но в данном случае незаменимый тип данных, как COMSafeArray.

Выдержка из синтакс-помощника:

COMSafeArray (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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *