При разработке внешних компонент по технологии Native API для платформы 1С:Предприятие разработчики часто сталкиваются с трудностями при возврате результатов вызова методов в платформу. Ошибки при заполнении универсальной структуры возвращаемого значения или некорректная работа с динамической памятью приводят к аварийному завершению работы клиента или сервера 1С с сообщением «некорректная работа компоненты с памятью». Разберем по шагам, как устроена структура передачи данных, как возвращать примитивные типы и строки, а также как безопасно передавать сложные структуры данных.
Для обмена данными между платформой 1С и нативной компонентой в методах CallAsFunc, CallAsProc, GetPropVal и SetPropVal используется универсальная структура tVariant. В метод функции указатель на результат обычно передается под именем pvarRetValue.
Поскольку pvarRetValue является указателем на структуру в языке C++, обращение к его полям выполняется через оператор стрелочки ->. Сама структура состоит из двух ключевых частей: поля типа vt (перечисление ENUMVARIANT) и безымянного объединения (union), содержащего поля конкретных значений.
Объединение (union) устроено так, что все его поля занимают одну и ту же область оперативной памяти. Поэтому одновременно в структуре может храниться значение только одного типа. Чтобы платформа 1С корректно прочитала данные, мы обязаны сначала указать тип значения в свойстве vt, а затем поместить само значение в соответствующее поле объединения.
Рассмотрим пример возврата целого числа (32 бита):
pvarRetValue->vt = VTYPE_I4;
pvarRetValue->lVal = 100;
Если требуется вернуть логическое значение, используем тип VTYPE_BOOL и поле bVal:
pvarRetValue->vt = VTYPE_BOOL;
pvarRetValue->bVal = true;
Для чисел с плавающей точкой повышенной точности (соответствует типу Число в 1С с дробной частью) используется VTYPE_R8 и поле dblVal:
pvarRetValue->vt = VTYPE_R8;
pvarRetValue->dblVal = 123.456;
Такие простые типы передаются по значению прямо через стек, поэтому дополнительное выделение динамической памяти для них не требуется.
Сложнее обстоит дело, когда требуется вернуть строковые или двоичные данные переменной длины. Выясним причину, по которой 1С падает с ошибкой памяти при попытке вернуть строку, созданную стандартными операторами C++ (такими как new или malloc).
Платформа 1С:Предприятие берет на себя полное управление жизненным циклом возвращаемых строк. Когда функция компоненты завершает работу, платформа забирает указатель на строку, читает её, а затем пытается освободить занятую память с помощью собственного механизма очистки. Если память была выделена через стандартный аллокатор C++, менеджер памяти платформы не сможет её корректно удалить, что вызовет фатальный сбой.
Для корректного выделения памяти 1С предоставляет компоненте специальный интерфейс менеджера памяти IMemoryManager. Разберем по шагам алгоритм правильного возврата строки:
Init сохраняем полученный указатель на интерфейс менеджера памяти в закрытую переменную класса компоненты (например, m_iMemory).AllocMemory с учетом размера символа и завершающего нуля.pvarRetValue, указав тип VTYPE_PWSTR, адрес буфера в pwstrVal и длину строки в символах в поле wstrLen.Проанализируем распространенные ошибки при заполнении строки в tVariant:
sizeof(result) вместо реальной длины строки. Если result объявлен как указатель, то sizeof вернет размер самого указателя (4 байта для x86 или 8 байт для x64), а не длину текстовой строки. Длину необходимо вычислять специальными функциями подсчета символов.strLen вместо wstrLen. В объединении tVariant поле strLen относится к однобайтовым строкам pstrVal, а для широких двухбайтовых строк pwstrVal обязательно должно заполняться поле wstrLen.delete[]. Выделенную под результат память освобождать нельзя — её освободит сама платформа 1С после обработки. При этом любые промежуточные временные буферы, созданные внутри метода, удалять через delete[] обязательно во избежание утечек памяти.Обратим внимание на нюанс кроссплатформенной работы. В заголовочных файлах Native API для 1С тип WCHAR_T жестко определен как двухбайтовый символ (UTF-16LE). В операционных системах Linux и macOS стандартный системный тип wchar_t языка C++ имеет размер 4 байта (UTF-32).
Поэтому прямое использование стандартных системных функций вроде mbstowcs на серверах под управлением Linux приведет к искажению данных. Для кроссплатформенного кода следует использовать преобразование строк в UTF-16 (через вспомогательные функции из шаблона ИТС convToShortWchar или стандартные средства современных стандартов C++).
Технология Native API имеет строгое архитектурное ограничение: передать напрямую объекты конфигурации 1С (такие как Структура, Массив или ТаблицаЗначений) через tVariant невозможно. Тип VTYPE_VARIANT не предназначен для сборки произвольных иерархических структур платформы.
Посмотрим на практические варианты решения этой задачи:
VTYPE_PWSTR. На стороне 1С полученная строка легко преобразуется во встроенные типы платформы с помощью стандартного метода ПрочитатьJSON().ExternalEvent интерфейса IAddInDefBase. Платформа 1С примет данные в процедуре модуля ОбработкаВнешнегоСобытия.В коде конфигурации 1С получение результата в виде JSON-строки и его разбор выглядит следующим образом:
РезультатСтрока = Компонента.ПолучитьДанные();
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(РезультатСтрока);
ДанныеОбъекта = ПрочитатьJSON(ЧтениеJSON);
ЧтениеJSON.Закрыть();
Соблюдение правил работы с объединением tVariant, использование менеджера памяти AllocMemory для строковых данных и применение формата JSON для сложных типов гарантируют стабильную и быструю работу нативной компоненты без утечек памяти и аварийных сбоев платформы 1С.