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

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

Разработка внешних компонент для платформы 1С:Предприятие на языке C# (.NET) — популярное решение, когда базовых возможностей встроенного языка не хватает. Однако разработчики часто сталкиваются с ситуацией: в толстом клиенте на компьютере разработчика всё работает прекрасно, но при попытке упаковать библиотеку в ZIP-архив (макет) и подключить её в тонком клиенте или на сервере через функцию ПодключитьВнешнююКомпоненту, платформа возвращает Ложь.

В этой статье мы подробно разберем, почему возникает эта проблема, проанализируем разницу между технологиями COM и Native API, выясним роль среды выполнения CLR и рассмотрим современные, надежные способы развертывания .NET-компонент без предварительной ручной регистрации в операционной системе.

В чем суть проблемы: анализ исходной ситуации

Посмотрим на представленный в теме код. Разработчик создал класс AddIn, который реализует интерфейсы IInitDone, ILanguageExtender, IAsyncEvent и IStatusLine. Эти интерфейсы имеют строго определенные GUID (например, [Guid("AB634001-F13D-11d0-A459-004095E1DAEA")]).

Главная архитектурная ошибка заключается в путанице технологий. Описанные интерфейсы — это классическая технология COM (Component Object Model), созданная Microsoft. Она использовалась в 1С еще со времен версии 7.7. Технология Native API, появившаяся позже, устроена совершенно иначе: она кроссплатформенная, не использует COM-интерфейсы, системный реестр Windows и GUID.

Когда разработчик пытается выполнить следующий код:


НачатьПодключениеВнешнююКомпоненты(ОбработчикПодключения, "ОбщийМакет.testDll", "Component", ТипВнешнейКомпоненты.Native);

Платформа 1С ожидает, что внутри динамической библиотеки (DLL) будут находиться экспортируемые функции C-формата (такие как GetClassObject, DestroyObject). Но так как библиотека написана на C# и скомпилирована под COM, этих точек входа в ней нет. Платформа не может их обнаружить и возвращает ошибку подключения.

Почему это работало в толстом клиенте?

Выясним причину, почему в толстом клиенте вызовы завершались успешно. На компьютере разработчика компонента была предварительно зарегистрирована в операционной системе с помощью утилиты regasm.exe или через настройки Visual Studio (Register for COM interop).

При ручной регистрации в реестре Windows Windows прописывается информация о ProgID и пути к файлу. В этом случае, даже если в коде 1С написано подключение из макета, платформа при неудаче локального поиска обращается к системному реестру, находит там зарегистрированный COM-класс и успешно создает объект. Но на компьютерах пользователей, где ручная регистрация не проводилась, этот механизм сразу же ломается.

Почему COM-компоненту на .NET нельзя установить «на лету» из макета?

Рассмотрим подробнее механизм работы 1С с COM-компонентами из макета:

  1. Платформа распаковывает DLL-файл из ZIP-архива во временный каталог на диске пользователя.
  2. Для COM-компоненты 1С пытается вызвать стандартную системную функцию саморегистрации библиотеки — DllRegisterServer.
  3. В классических C++ библиотеках эта функция присутствует и прописывает необходимые ключи в реестр текущего пользователя (HKCU\Software\Classes).
  4. Проблема .NET-сборок: Скомпилированные библиотеки на C# состоят из промежуточного байт-кода MSIL (CIL). Они не содержат нативного ассемблерного кода и экспорта функции DllRegisterServer. Регистрация таких библиотек должна осуществляться утилитой regasm.exe, которая создает в реестре Windows ссылки на общеязыковую среду выполнения (CLR / mscoree.dll). 1С вызывать regasm.exe в процессе работы не умеет и не имеет для этого прав администратора.

Таким образом, полноценное автоматическое подключение стандартной .NET COM-библиотеки через макет силами одной лишь платформы 1С невозможно.

---

Варианты решения проблемы

Проанализируем три основных архитектурных подхода, которые позволяют решить данную задачу и запустить C# код на стороне клиента или сервера.

Вариант 1. Переход на настоящий Native API с использованием .NET Core и Native AOT

С выходом современных версий платформы .NET (начиная с .NET 7 и .NET 8) появилась технология Native AOT (Ahead-Of-Time). Она компилирует код C# напрямую в машинный код целевой операционной системы (Windows, Linux, macOS). Полученная DLL не требует установленного .NET Runtime на целевом компьютере и ведет себя как классическая библиотека, написанная на C++ или Rust.

Благодаря этому мы можем объявить внутри C# необходимые точки экспорта для 1С Native API с помощью атрибута [UnmanagedCallersOnly].

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


using System;
using System.Runtime.InteropServices;

namespace MyNativeComponent
{
    public static class Exports
    {
        [UnmanagedCallersOnly(EntryPoint = "GetClassNames")]
        public static IntPtr GetClassNames()
        {
            // Возвращаем имена классов, поддерживаемых компонентой (в формате UTF-16/WCHAR)
            return Marshal.StringToCoTaskMemUni("MyComponentClass");
        }

        [UnmanagedCallersOnly(EntryPoint = "GetClassObject")]
        public static int GetClassObject(IntPtr className, ref IntPtr classObject)
        {
            string name = Marshal.PtrToStringUni(className);
            if (name == "MyComponentClass")
            {
                // Создаем экземпляр нашего класса, реализующего IComponentBase
                var component = new MyComponent();
                classObject = Marshal.GetComInterfaceForObject<MyComponent, IComponentBase>(component);
                return 0; // S_OK
            }
            return -1; // E_FAIL
        }

        [UnmanagedCallersOnly(EntryPoint = "DestroyObject")]
        public static int DestroyObject(ref IntPtr classObject)
        {
            if (classObject != IntPtr.Zero)
            {
                Marshal.Release(classObject);
                classObject = IntPtr.Zero;
                return 0; // S_OK
            }
            return -1; // E_FAIL
        }
    }
}

При таком подходе библиотека упаковывается в ZIP-архив с манифестом Manifest.xml следующего вида:


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

Эта компонента без проблем подключится методом ПодключитьВнешнююКомпоненту в любом клиенте 1С (включая веб-клиент или Linux-сервер, если собрать соответствующую версию библиотеки под Linux ELF).

Вариант 2. Использование Native C++ обертки (CLR Hosting)

Если вам необходимо работать в рамках .NET Framework (например, требуется совместимость со старым кодом или специфичными библиотеками) и Native AOT вам не подходит, можно использовать гибридный подход.

Суть метода заключается в следующем:

  1. Создается небольшая «прокси»-библиотека на C++ (Native API). Она без проблем загружается 1С из макета, так как является нативной.
  2. При инициализации эта C++ обертка запускает среду выполнения .NET (CLR Hosting) внутри адресного пространства процесса 1С (будь то 1cv8.exe тонкого клиента или rphost.exe сервера).
  3. C++ обертка динамически загружает вашу основную C# DLL (которую можно хранить рядом или даже упаковать внутрь ресурсов C++ DLL) и транслирует все вызовы методов и свойств из 1С в управляемый .NET-код.

Этот подход успешно реализован во многих публичных проектах (например, компоненты класса NETLoader). Для разработчика на 1С это выглядит как прозрачная работа с .NET-кодом без какой-либо предварительной регистрации библиотек в Windows.

Вариант 3. Использование RegFree COM (Side-by-Side Manifests)

Если вы хотите оставить архитектуру COM (использование интерфейсов IInitDone и ILanguageExtender) и подключать компоненту через Новый COMОбъект, но при этом избежать вызова утилиты regasm.exe с правами администратора на каждом клиентском компьютере, можно настроить технологию RegFree COM.

Для этого выполняются следующие шаги:

  1. Создается специальный файл манифеста приложения для платформы 1С или манифест сборки для DLL, в котором прописывается соответствие между вашим ProgID/CLSID и физическим расположением DLL на диске.
  2. При обращении к объекту Windows считывает информацию не из глобального реестра, а напрямую из манифеста, и загружает нужные зависимости.

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

---

Резюме и рекомендации

Подведем итог. Попытка загрузить .NET COM-библиотеку напрямую через макет с типом Native или COM без предварительной системной регистрации всегда будет завершаться ошибкой. Для решения этой проблемы выберите один из путей:

← На главную