Как написать и успешно подключить внешнюю компоненту на C# для 1С:Предприятия 8

Программист 1С v8.3 (Управляемые формы) 1С:Документооборот IT и автоматизация бизнеса
← На главную

Интеграция приложений на платформе .NET (C#) с системой «1С:Предприятие 8» — одна из наиболее востребованных и одновременно сложных задач для разработчика. Традиционно перед программистом встает выбор между использованием технологии COM и кроссплатформенным стандартом Native API. В этой статье мы подробно разберем, как обойти технологические ограничения, создать полноценную внешнюю компоненту на C#, правильно упаковать ее в макет 1С и избежать классических ошибок инициализации.

COM против Native API: в чем сложность для .NET

Прежде чем перейти к практике, разберем теоретические основы взаимодействия сред выполнения. Исторически среда 1С общается с внешним миром двумя путями:

  1. COM (Component Object Model) — технология, жестко привязанная к операционной системе Windows. Она требует обязательной регистрации библиотек в системном реестре (через утилиту regasm или regsvr32), что накладывает ограничения на права пользователей и полностью исключает работу под Linux.
  2. Native API — открытый кроссплатформенный интерфейс 1С, основанный на прямом вызове экспортируемых C-функций из динамической библиотеки (DLL под Windows или SO под Linux).

Сложность для C# заключается в том, что стандартный компилятор .NET собирает код в промежуточный язык MSIL (Intermediate Language). Для его работы требуется виртуальная машина (CLR), поэтому обычную сборку .NET нельзя просто так загрузить в 1С как Native API компоненту. Тем не менее, существуют эффективные обходные пути: использование специализированных мостов-оберток (например, технологии Elisy или классов-конвертеров) и современная компиляция Native AOT в .NET.

Анализ кода компоненты и разбор типичной ошибки

Рассмотрим пример кода внешней компоненты, написанной на C# с использованием обертки Native API, которая часто вызывает проблемы у разработчиков:


using System.Runtime.InteropServices;

namespace V8.AddIn
{
    public class SomeName
    {
        public SomeName()
        {
        }

        [Alias("Выполнить")]
        public string ProcessString(string text)
        {
            return "Hello world from c#";
        }
    }

    [Guid("6a81d0a9-6441-463f-a0c9-ec7b1f2cbd56")]
    [ProgId("AddIn.SomeComponent")]
    [ComVisible(true)]
    public class Some : LanguageExtenderAddIn
    {
        public Some() : base(typeof(SomeName), 1000)
        {
        }
    }
}

При попытке вызвать эту компоненту в коде 1С стандартным образом:


ПодключитьВнешнююКомпоненту("AddIn.SomeComponent");
ОбъектКомпоненты = Новый("AddIn.SomeName");        
ОбъектКомпоненты.Выполнить("Быстро"); 

Платформа выдает критическую ошибку: «Тип не определен (AddIn.SomeName)». Давайте выясним причины этого сбоя и исправим их.

Почему возникает ошибка «Тип не определен» и как ее исправить

Эта ошибка является следствием неверного понимания адресации объектов во внешних компонентах Native API. Проанализируем три главные причины проблемы:

1. Ошибка в синтаксисе обращения к объекту в 1С

При использовании Native API структура инициализации жестко регламентирована. Метод ПодключитьВнешнююКомпоненту принимает путь к макету или файлу и регистрирует символическое имя компоненты (алиас подключения). В коде выше в качестве первого параметра передан ProgId "AddIn.SomeComponent". В этом случае имя подключения регистрируется как SomeComponent.

Чтобы создать экземпляр класса компоненты, необходимо обращаться по схеме: Новый("AddIn.ИмяПодключения.ИмяКласса"). Автор кода ошибочно попытался создать объект напрямую по имени класса: Новый("AddIn.SomeName"), пропустив имя подключения. Правильный вызов должен выглядеть так:


// Подключаем компоненту по имени из макета
ПодключитьВнешнююКомпоненту("ОбщийМакет.eSignPushTest", "MyComponent", ТипВнешнейКомпоненты.Native);

// Создаем объект, указывая ИмяПодключения и ИмяКласса через точку
ОбъектКомпоненты = Новый("AddIn.MyComponent.Some"); 

2. Несоответствие разрядности платформы (32/64 бит)

Платформа 1С:Предприятие крайне чувствительна к разрядности загружаемых динамических библиотек. Если вы используете 64-битную операционную систему Windows, это еще не означает, что ваш клиент 1С работает в 64-битном режиме. Тонкие и толстые клиенты очень часто устанавливаются как 32-битные приложения (папка x86).

Если dll скомпилирована строго под платформу x64, а клиент 1С запущен в режиме x86 (или наоборот), платформа не сможет загрузить библиотеку на низком уровне и выдаст ошибку отсутствия типа. Рекомендуется компилировать компоненту в двух вариантах и правильно описывать их в манифесте сборки.

3. Ошибки в манифесте внешней компоненты

Для корректного подключения компоненты из общего макета 1С требуется файл манифеста MANIFEST.XML, который упаковывается вместе с DLL-файлами в один ZIP-архив. Посмотрим на структуру корректного манифеста, поддерживающего обе архитектуры под Windows:


<?xml version="1.0" encoding="UTF-8" ?>
<bundle xmlns="http://v8.1c.ru/8.2/addin/bundle">
    <component os="Windows" path="eSignPushTest_x86.dll" type="native" arch="i386" />
    <component os="Windows" path="eSignPushTest_x64.dll" type="native" arch="x86_64" />
</bundle>

Обратите внимание на атрибут arch: значение i386 соответствует 32-битному клиенту, а x86_64 — 64-битному. Файлы библиотек должны физически находиться в корне ZIP-архива рядом с файлом манифеста.

Современное решение: Native AOT в .NET 8 и старше

Если вы хотите создать действительно производительную, кроссплатформенную и автономную внешнюю компоненту Native API на C# без использования тяжелых COM-оберток и сторонних зависимостей, следует обратить внимание на технологию Native AOT (Ahead-of-Time compilation), появившуюся в современном .NET.

Native AOT компилирует код C# напрямую в машинный код (машинные инструкции целевой ОС). На выходе мы получаем стандартную библиотеку (DLL или SO), которая больше не требует установленного .NET Runtime на целевом компьютере и может напрямую экспортировать функции для 1С.

Разберем, как выглядит экспорт основных функций Native API в проекте C#:


using System.Runtime.InteropServices;

namespace MyNativeAddIn
{
    public static class EntryPoints
    {
        [UnmanagedCallersOnly(EntryPoint = "GetClassNames")]
        public static unsafe int GetClassNames(IntPtr names)
        {
            // Возвращаем имена классов компоненты для 1С
            return 1;
        }

        [UnmanagedCallersOnly(EntryPoint = "GetClassObject")]
        public static unsafe int GetClassObject(IntPtr name, IntPtr obj)
        {
            // Инициализируем объект компоненты
            return 1;
        }

        [UnmanagedCallersOnly(EntryPoint = "DestroyObject")]
        public static unsafe int DestroyObject(IntPtr obj)
        {
            // Освобождаем память объекта
            return 1;
        }
    }
}

Использование атрибута UnmanagedCallersOnly позволяет среде 1С видеть методы .NET библиотеки так же, как если бы они были написаны на C++. Это полностью решает проблему кроссплатформенности: скомпилированная под Linux (.so) компонента будет без проблем работать на сервере 1С под управлением Linux.

Безопасное и изолированное подключение в 1С

Разработчикам внешних компонент важно помнить о стабильности работы всей системы. Если в коде C# компоненты произойдет непредвиденная ошибка (например, переполнение буфера при работе с сокетами или утечка памяти при обработке изображений), это может привести к аварийному завершению работы всего сеанса 1С или рабочего процесса сервера rphost.

Чтобы обезопасить систему, рекомендуется использовать встроенный в платформу 1С механизм изолированного подключения. Для этого при вызове метода подключения передавайте соответствующий параметр:


&НаКлиенте
Процедура ПодключитьИзолированно()
    
    ИмяФайлаКомпоненты = "ОбщийМакет.eSignPushTest";
    
    Попытка
        // Подключаем компоненту в изолированном процессе ОС
        ПодключитьВнешнююКомпоненту(ИмяФайлаКомпоненты, "eSign", ТипВнешнейКомпоненты.Native, , СпособПодключенияВнешнейКомпоненты.Изолированно);
        КомпонентаПодключена = Истина;
    Исключение
        Предупреждение("Не удалось подключить компоненту: " + ОписаниеОшибки());
    КонецПопытки;

    Если КомпонентаПодключена Тогда
        Попытка
            ОбъектСДК = Новый("AddIn.eSign.Some");
            Результат = ОбъектСДК.Выполнить("Параметр");
            Сообщить(Результат);
        Исключение
            Сообщить("Ошибка вызова метода компоненты: " + ОписаниеОшибки());
        КонецПопытки;
    КонецЕсли;
    
КонецПроцедуры

При изолированном подключении операционная система запускает компоненту в выделенном процессе. Любой критический сбой внутри C# кода изолируется и не приводит к падению клиентского приложения 1С, гарантируя бесперебойную работу пользователя.

← На главную