Внешняя библиотека в коде 1С 8.3: три рабочих способа и подводные камни
Содержание
Произвольную функцию из DLL встроенный язык 1С напрямую не вызывает: аналога Declare из VBA в нём нет. Использовать DLL в 1С можно тремя путями — оформить её как внешнюю компоненту по технологии Native API и подключить через ПодключитьВнешнююКомпоненту(), зарегистрировать как COM-сервер и создать через Новый COMОбъект("ProgID"), либо вызвать через промежуточную программу.
Какой путь выбрать, зависит от того, кто писал библиотеку и где она будет работать: на клиенте, на сервере, под Windows или Linux. Если исходники есть — почти всегда выгоднее Native API: компонента лежит в макете конфигурации и не требует регистрации в реестре.
Что такое DLL с точки зрения 1С и когда она нужна
DLL — динамическая библиотека Windows с готовым кодом. Для платформы 1С важна не сама DLL, а интерфейс, через который с ней можно разговаривать. Платформа понимает два таких интерфейса:
- 🧩 Native API — собственный протокол 1С для внешних компонент. Библиотека экспортирует строго определённые функции, платформа видит её объекты как
AddIn.Имя.Объект. - 🔌 COM — стандартная технология Windows. Библиотека регистрируется в реестре, а 1С создаёт её объект по ProgID.
Обычная DLL с функциями вроде int Sum(int a, int b) не подходит ни под один из них. Её придётся обернуть: написать тонкую компоненту Native API или COM-класс, который внутри вызывает нужную функцию.
Типичные задачи, ради которых это делают:
- 🖨️ работа с оборудованием: сканеры штрихкодов, весы, фискальные регистраторы, считыватели карт;
- 🔐 криптография и подпись по протоколам, которых нет в платформе;
- ⚙️ тяжёлые вычисления, которые на встроенном языке идут слишком медленно;
- 🏦 SDK банков, служб доставки, систем контроля доступа, поставляемые как DLL;
- 🪟 вызов функций Windows API: работа с окнами, принтерами, оборудованием.
Способы подключения DLL в 1С: сравнение
| Способ | Когда подходит | Где работает | Риски |
|---|---|---|---|
| Внешняя компонента Native API | Пишете библиотеку сами или есть исходники | Windows и Linux, клиент и сервер, веб-клиент с расширением браузера | Нужно знать C++ и SDK 1С; ошибка в компоненте роняет процесс |
| COM-объект | Готовая COM-библиотека или .NET-сборка | Только Windows | Регистрация на каждом компьютере, проблемы разрядности |
| Внешняя программа | DLL чужая и без исходников, вызовы редкие | Там, где можно запустить exe | Медленно: запуск процесса на каждый вызов |
Способ 1. Внешняя компонента Native API
Это основной способ для новых разработок. Компоненту упаковывают в ZIP-архив: внутри DLL для Windows разной разрядности, при необходимости .so для Linux, и файл MANIFEST.XML с описанием, какой файл под какую платформу.
Архив кладут в общий макет конфигурации с типом «Внешняя компонента» (или «Двоичные данные»). Дальше компонента подключается по имени макета.
Пример на сервере, где установка компоненты не требуется:
&НаСервереБезКонтекста
Функция СложитьЧерезКомпоненту(А, Б)
Подключена = ПодключитьВнешнююКомпоненту("ОбщийМакет.МояКомпонента", "Моя",
ТипВнешнейКомпоненты.Native);
Если Не Подключена Тогда
ВызватьИсключение "Не удалось подключить внешнюю компоненту МояКомпонента";
КонецЕсли;
Калькулятор = Новый("AddIn.Моя.Калькулятор");
Возврат Калькулятор.Сложить(А, Б);
КонецФункции
Строка AddIn.Моя.Калькулятор собирается из имени, переданного вторым параметром (Моя), и имени класса, которое компонента сообщает платформе (Калькулятор). Ошибка в любой части даёт исключение при Новый.
На клиенте компоненту сначала устанавливают на компьютер пользователя, затем подключают. В конфигурациях с запретом синхронных вызовов используйте асинхронные методы. Вариант с Асинх/Ждать работает на платформе 8.3.18 и выше:
&НаКлиенте
Перем Компонента;
&НаКлиенте
Асинх Процедура ПодключитьКомпонентуНаКлиенте()
Если Компонента <> Неопределено Тогда
Возврат;
КонецЕсли;
Местоположение = "ОбщийМакет.МояКомпонента";
Подключена = Ждать ПодключитьВнешнююКомпонентуАсинх(Местоположение, "Моя",
ТипВнешнейКомпоненты.Native);
Если Не Подключена Тогда
Ждать УстановитьВнешнююКомпонентуАсинх(Местоположение);
Подключена = Ждать ПодключитьВнешнююКомпонентуАсинх(Местоположение, "Моя",
ТипВнешнейКомпоненты.Native);
КонецЕсли;
Если Подключена Тогда
Компонента = Новый("AddIn.Моя.Калькулятор");
Иначе
Сообщить("Компонента не подключена: проверьте разрядность клиента и состав архива");
КонецЕсли;
КонецПроцедуры
Если подключение не удаётся только у части пользователей, первым делом сравните разрядность их клиента 1С с составом архива. 64-битный клиент не загрузит 32-битную DLL.
Способ 2. DLL как COM-объект
Подходит для готовых COM-библиотек и сборок .NET. Библиотеку регистрируют в Windows из командной строки, запущенной от имени администратора.
Для обычной COM-DLL:
rem 64-битная библиотека
C:\Windows\System32\regsvr32.exe "C:\Libs\MyLib.dll"
rem 32-битная библиотека на 64-битной Windows
C:\Windows\SysWOW64\regsvr32.exe "C:\Libs\MyLib.dll"
Для сборки .NET Framework используется RegAsm нужной разрядности:
%WINDIR%\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe "C:\Libs\MyNetLib.dll" /codebase /tlb
В самой сборке класс должен быть видим для COM: атрибуты [ComVisible(true)], [Guid("…")] и [ProgId("MyNetLib.Calculator")], открытый конструктор без параметров.
Вызов из 1С:
Попытка
Калькулятор = Новый COMОбъект("MyNetLib.Calculator");
Результат = Калькулятор.Sum(2, 3);
Исключение
ВызватьИсключение "COM-объект не создан: " + ОписаниеОшибки();
КонецПопытки;
Регистрировать библиотеку нужно на каждом компьютере, где выполняется код: на клиентских машинах для &НаКлиенте и на всех рабочих серверах кластера для &НаСервере.
Способ 3. Через промежуточную программу
Если DLL сторонняя, исходников нет и это не COM-сервер, напишите маленькую консольную программу, которая вызывает функцию и печатает результат в файл или stdout. Из 1С её запускают через ЗапуститьПриложение() с ожиданием завершения и читают результат.
Ещё один вариант — сторонние COM-обёртки для вызова произвольных функций Windows API. Они работают, но это чужой код без поддержки: проверьте лицензию и разрядность до использования в рабочей базе.
Передача параметров: соответствие типов 1С и DLL
Больше всего ошибок возникает на стыке типов. Встроенный язык знает свои типы, библиотека — свои, а платформа переводит одно в другое по фиксированным правилам.
Типы в Native API
Параметры приходят в компоненту массивом структур tVariant. Тип каждого значения нужно проверять перед чтением.
| Тип 1С | Тип в компоненте | Что учесть |
|---|---|---|
| Число | VTYPE_I4 или VTYPE_R8 | Целое может прийти как I4, дробное — как R8. Обрабатывайте оба |
| Строка | VTYPE_PWSTR | Двухбайтные символы WCHAR_T, длина в поле wstrLen |
| Булево | VTYPE_BOOL | — |
| Дата | VTYPE_TM | Структура tm, без часового пояса |
| ДвоичныеДанные | VTYPE_BLOB | Удобно для передачи файлов и больших массивов |
| Неопределено | VTYPE_EMPTY | Пропущенный необязательный параметр |
Фрагмент метода компоненты на C++, который складывает два числа:
bool CAddInNative::CallAsFunc(const long lMethodNum, tVariant* pvarRetValue,
tVariant* paParams, const long lSizeArray)
{
if (lMethodNum == eMethSum && lSizeArray == 2)
{
if (TV_VT(&paParams[0]) != VTYPE_I4 || TV_VT(&paParams[1]) != VTYPE_I4)
return false; // платформа выдаст ошибку вызова метода
TV_VT(pvarRetValue) = VTYPE_I4;
TV_I4(pvarRetValue) = TV_I4(&paParams[0]) + TV_I4(&paParams[1]);
return true;
}
return false;
}
Возвращаемую строку или двоичные данные выделяйте только через менеджер памяти платформы — метод AllocMemory интерфейса IMemoryManager. Память, выделенная через new или malloc, приведёт к падению при освобождении.
Типы в COM
Через COM платформа передаёт Число как целое или double, Строку как BSTR, Булево как bool, Дату как DATE. Для массивов есть специальный тип COMSafeArray: обычный Массив 1С не передаётся в метод, ожидающий SAFEARRAY.
Данные = Новый COMSafeArray("VT_I4", 3);
Данные.SetValue(0, 10);
Данные.SetValue(1, 20);
Данные.SetValue(2, 30);
Сумма = Калькулятор.SumArray(Данные);
💡 Структуры, указатели и колбэки через COM из 1С не передать. Если функция DLL ждёт struct*, разложите структуру на простые параметры в своей обёртке.
Обработка ошибок и отладка библиотеки
Ошибки на стороне 1С
- ❌
ПодключитьВнешнююКомпоненту()вернулаЛожь— неверное имя макета, повреждённый архив, нет DLL под нужную разрядность или платформу вMANIFEST.XML. - ❌ Исключение на
Новый("AddIn…")— опечатка в имени объекта или компонента не подключена в этом контексте. Подключение на клиенте не действует на сервере, и наоборот. - ❌
Новый COMОбъектпадает с ошибкой создания объекта — библиотека не зарегистрирована на этом компьютере или зарегистрирована не той разрядности. - ❌ Метод не найден — у Native API имена методов задаёт сам разработчик компоненты (обычно русское и английское), у COM — только имя из библиотеки.
Все вызовы внешнего кода оборачивайте в Попытка … Исключение и пишите подробности в журнал регистрации:
Попытка
Результат = Компонента.Выполнить(Параметр);
Исключение
Инфо = ИнформацияОбОшибке();
ЗаписьЖурналаРегистрации("ВнешняяКомпонента.Вызов", УровеньЖурналаРегистрации.Ошибка,,,
ПодробноеПредставлениеОшибки(Инфо));
ВызватьИсключение;
КонецПопытки;
Ошибки на стороне компоненты
Компонента Native API может сообщить об ошибке
через метод AddError интерфейса IAddInDefBase. В зависимости от кода ошибки платформа либо выводит сообщение, либо вызывает исключение, которое перехватывается обычной Попытка.
Исключение C++, вылетевшее из компоненты наружу, платформа не перехватит. Ловите всё внутри каждого метода.
Отладка
- Соберите компоненту в отладочной конфигурации с файлами символов (
.pdb). - Запустите 1С и подключите компоненту хотя бы один раз.
- В Visual Studio выберите подключение к процессу:
1cv8c.exeили1cv8.exeдля клиентского кода,rphost.exeдля серверного. - Поставьте точку останова в методе и вызовите его из 1С.
Для серверной отладки удобнее файловая база: код &НаСервере тогда выполняется внутри процесса клиента, и искать нужный rphost не придётся.
Подключение отладчика к rphost.exe на рабочем сервере останавливает обработку запросов всех пользователей, чьи сеансы обслуживает этот процесс. Отлаживайте на тестовом сервере.
Как ускорить работу с внешней библиотекой
- ⚡ Подключайте один раз. На клиенте храните объект компоненты в переменной модуля формы или модуля приложения, как в примере выше. Создание объекта на каждый вызов дорого.
- 📦 Передавайте пачками. Тысяча вызовов по одному значению медленнее одного вызова с тысячей значений. Собирайте данные в строку JSON или
ДвоичныеДанныеи разбирайте внутри DLL. - 🔁 Сокращайте COM-обращения. Каждое чтение свойства COM-объекта — отдельный вызов. Значение, которое нужно в цикле, прочитайте один раз в переменную.
- 🖥️ Переносите тяжёлое на сервер. Если компонента не работает с локальным оборудованием, вызывайте её на сервере: не придётся ставить её на клиенты, и не будет лишних клиент-серверных вызовов.
- 🧵 Не держите поток. Долгую операцию внутри DLL лучше запускать в отдельном потоке компоненты и сообщать о завершении внешним событием (
ExternalEvent), которое ловит процедураОбработкаВнешнегоСобытияформы.
Примеры: что подключают к 1С через DLL
Работа с COM-объектами Windows. Самый частый практический случай — системные COM-библиотеки, которые уже зарегистрированы в Windows:
&НаКлиенте
Процедура ОткрытьПапкуВыгрузки(Команда)
Оболочка = Новый COMОбъект("WScript.Shell");
Оболочка.Run("explorer.exe ""C:\Выгрузка""", 1, Ложь);
КонецПроцедуры
Через Scripting.FileSystemObject так же получают свойства дисков, а через Excel.Application — управляют Excel, если он установлен.
Своя .NET-библиотека. Например, проверка подписи или расчёт по формуле банка. Пишете класс на C#, помечаете атрибутами COM, регистрируете RegAsm на сервере — и вызываете из общего модуля.
Компонента для оборудования. Драйверы оборудования для типовых конфигураций поставляются как внешние компоненты Native API. Подсистема подключаемого оборудования сама устанавливает и подключает их, поэтому код писать не нужно — достаточно добавить устройство в настройках.
Обёртка над чужой DLL. Есть SDK сканера отпечатков с функциями на C. Пишете компоненту Native API с двумя методами — Инициализировать и Сканировать, — которая внутри вызывает функции SDK через LoadLibrary и GetProcAddress. В 1С остаётся пять строк кода.
Безопасность и ограничения внешних библиотек
Безопасный режим и профили безопасности
В безопасном режиме создание COM-объектов и подключение внешних компонент запрещено. Под это ограничение попадают внешние отчёты и обработки, которые конфигурация запускает в безопасном режиме, а также код, выполняемый внутри УстановитьБезопасныйРежим(Истина).
Если на кластере включены профили безопасности, разрешение на внешние компоненты и COM-классы задаётся в профиле. Без него код упадёт даже в обычном режиме.
Разрядность
32-битная DLL не загружается в 64-битный процесс. Это касается и клиента 1С, и рабочих процессов сервера.
- для Native API включайте в архив сборки под обе разрядности;
- для 32-битной COM-библиотеки на 64-битном сервере можно запускать её вне процесса через суррогатный процесс Windows
dllhost.exe(настройка DllSurrogate или приложение COM+); - лучше всего — пересобрать библиотеку под x64.
Ограничения клиентов
| Где выполняется код | Native API | COM |
|---|---|---|
| Тонкий и толстый клиент, Windows | Да | Да |
| Тонкий клиент, Linux | Да, если в архиве есть .so | Нет |
| Веб-клиент | Да, с расширением для работы с компонентами в браузере | Нет |
| Сервер на Windows | Да | Да |
| Сервер на Linux | Да, если в архиве есть .so | Нет |
Стабильность и доверие
Компонента работает в адресном пространстве процесса 1С. Ошибка в DLL на сервере роняет рабочий процесс rphost вместе со всеми сеансами, которые он обслуживает. Пользователи увидят разрыв соединения и потеряют несохранённые данные.
Отсюда простые правила:
- Подключайте только библиотеки из известного источника и с проверенной версией.
- Нестабильный или чужой код выносите в отдельный процесс: программу-посредник или COM-сервер вне процесса.
- Перед выкладкой на рабочий сервер прогоняйте компоненту под нагрузкой на копии базы.