Как обновить и подключить внешнюю компоненту в 1С без прав администратора?

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

При разработке и поддержке корпоративных информационных систем на платформе 1С:Предприятие программисты часто сталкиваются с жесткими политиками безопасности. Типичная ситуация: пользователям на удаленных рабочих столах (RDP) или локальных ПК запрещено вносить изменения в каталог Program Files, регистрировать библиотеки в системном реестре (для COM-объектов) и выполнять любые действия под учетной записью администратора. В таких условиях обновление самописной внешней компоненты (ВК), особенно если она взаимодействует со сторонними консольными утилитами (EXE-файлами), превращается в нетривиальную задачу.

В этой статье мы подробно разберем, как реализовать механизм бесшовного обновления и подключения внешних компонент типа NativeAPI без участия системных администраторов, проанализируем ошибки в коде инициализации, изучим особенности системных каталогов Windows и предложим надежные архитектурные решения.

Анализ и исправление критической ошибки в коде подключения

Для начала проанализируем листинг кода, который часто используется для подключения компоненты из макета. В оригинальном примере автора темы присутствует критическая логическая ошибка, из-за которой внешняя компонента физически не может получить параметры (в частности, путь к исполняемому файлу):


Макет = ПолучитьМакет("ВК");    
Адрес = ПоместитьВоВременноеХранилище(Макет); 
ПодключениеВыполнено = ПодключитьВнешнююКомпоненту(Адрес, "VK", ТипВнешнейКомпоненты.Native );
Если ПодключениеВыполнено Тогда 
    мОбъектАндроид = Новый("AddIn.VK.VKExtention");
    мОбъектВК.РабочаяПапка = ПутьКЕХЕ; // Ошибка: обращение к неопределенной переменной!
    СообщениеИнф("Подключение библиотеки ВК v" + мОбъектВК.Версия);
    Возврат мОбъектВК;
Иначе
    Возврат Неопределено;
КонецЕсли;

Выясним причину сбоя: при создании объекта компоненты экземпляр присваивается переменной мОбъектАндроид, однако последующая настройка свойств (РабочаяПапка) и чтение версии происходят через переменную мОбъектВК. Если в модуле не объявлена глобальная переменная с таким именем, платформа вызовет ошибку времени выполнения (Runtime Error), либо свойства просто не будут применены к вновь созданному объекту. В результате компонента не сможет обнаружить внешний EXE-файл, так как путь к нему останется пустым.

Исправим этот код и приведем его к правильному, отказоустойчивому виду:


// Безопасное подключение и инициализация внешней компоненты
МакетКомпоненты = ПолучитьМакет("ВК_NativeAPI");    
АдресВременногоХранилища = ПоместитьВоВременноеХранилище(МакетКомпоненты); 

ИмяКомпонентыНаДиске = "VK";
ПодключениеВыполнено = ПодключитьВнешнююКомпоненту(АдресВременногоХранилища, ИмяКомпонентыНаДиске, ТипВнешнейКомпоненты.Native);

Если ПодключениеВыполнено Тогда 
    Попытка
        // Создаем экземпляр объекта компоненты с корректным именем переменной
        НовыйОбъектВК = Новый("AddIn." + ИмяКомпонентыНаДиске + ".VKExtention");
        
        // Передаем рабочий каталог для запуска стороннего EXE
        НовыйОбъектВК.РабочаяПапка = ПутьКЕХЕ;
        
        ВызватьИсключениеОбновления = Ложь;
        // Проверяем версию компоненты при необходимости
        Если НовыйОбъектВК.Версия < МинимальноНеобходимаяВерсия Тогда
            ВызватьИсключениеОбновления = Истина;
        КонецЕсли;
        
        Если Не ВызватьИсключениеОбновления Тогда
            Возврат НовыйОбъектВК;
        КонецЕсли;
    Исключение
        ЗаписатьЛогСобытий("Ошибка инициализации объекта ВК: " + ОписаниеОшибки());
        Возврат Неопределено;
    КонецПопытки;
Иначе
    ЗаписатьЛогСобытий("Не удалось подключить внешнюю компоненту из временного хранилища.");
    Возврат Неопределено;
КонецЕсли;

Штатный механизм работы 1С с компонентами без прав администратора

Многие разработчики ошибочно полагают, что для работы внешних компонент обязательно требуется доступ к каталогу установки платформы в Program Files или регистрация библиотек в реестре. На самом деле платформа 1С:Предприятие имеет продуманный встроенный механизм для работы в условиях ограниченных прав пользователей.

Рассмотрим подробнее, как это функционирует изнутри:

  1. Автоматическая распаковка в профиль пользователя: При вызове методов УстановитьВнешнююКомпоненту() или ПодключитьВнешнююКомпоненту() платформа не пытается писать данные в системные защищенные папки. Вместо этого файлы распаковываются в подкаталоги личного профиля текущего пользователя операционной системы. Обычно это пути вида:
    • %APPDATA%\1C\1Cv8\ExtCompT\ (для современных версий платформы 8.3)
    • %LOCALAPPDATA%\Temp\1Cv8\ (для временных файлов сессии)
    Так как эти каталоги находятся внутри домашней папки пользователя (%USERPROFILE%), операционная система по умолчанию предоставляет текущему пользователю полные права на чтение, запись и изменение файлов в них. Никаких прав локального администратора не требуется.
  2. Автоматическое версионирование и обновление: Если внешняя компонента упакована в ZIP-архив вместе со специальным манифестом MANIFEST.XML, платформа 1С берет на себя задачу контроля версий. При вызове метода УстановитьВнешнююКомпоненту() платформа сравнивает версию компоненты, хранящуюся в базе данных (в макете), с версией файла, находящегося на диске компьютера пользователя. Если версия в макете новее, 1С автоматически перезапишет DLL-файл в каталоге %APPDATA% «на лету», освобождая старую библиотеку из оперативной памяти процесса.

Почему внешняя компонента может «не видеть» EXE-файл?

Если синтаксические ошибки в коде 1С устранены, но компонента все равно выдает ошибку о невозможности найти или запустить внешний исполняемый файл, следует проанализировать инфраструктурные факторы:

Рекомендуемое архитектурное решение (Zero-Touch Deployment)

Чтобы полностью исключить человеческий фактор и необходимость предварительной подготовки рабочих мест (ручного копирования EXE-файлов на каждый компьютер), рекомендуется использовать концепцию "Единого архива". Разберем этот подход по шагам:

  1. Подготовка ZIP-архива: Поместите в один ZIP-архив не только DLL-библиотеку внешней компоненты, но и сам исполняемый файл сторонней утилиты вместе со всеми необходимыми ей конфигурационными файлами или библиотеками зависимостей.
  2. Написание файла MANIFEST.XML: Добавьте в корень архива файл манифеста, в котором будут описаны все файлы компоненты и их версии. Пример структуры манифеста:
    
    <?xml version="1.0" encoding="UTF-8"?>
    <bundle xmlns="http://v8.1c.ru/8.2/addin/bundle">
        <component id="MyCustomComponent" type="native" path="MyComponentDLL.dll" os="Windows" x86="true" x64="true" />
    </bundle>
    
  3. Автоматическое извлечение: При вызове метода УстановитьВнешнююКомпоненту() платформа 1С автоматически распакует в подкаталог папки %APPDATA% абсолютно все файлы, содержащиеся в ZIP-архиве. Таким образом, и DLL, и нужный EXE-файл гарантированно окажутся в одной и той же папке на диске пользователя.
  4. Определение путей внутри C++ DLL: Напишите код вашей NativeAPI-компоненты так, чтобы она динамически определяла каталог своего запуска. В операционной системе Windows путь к текущей DLL можно легко получить с помощью вызова функции Windows API GetModuleFileName, передав в качестве дескриптора модуля адрес загруженной библиотеки. Получив этот путь, компонента может запустить EXE-файл, лежащий в этой же папке, по абсолютному пути, полностью избавив код 1С от необходимости передавать параметр РабочаяПапка.

Альтернативное решение без использования внешних компонент

Если единственная цель создания внешней компоненты — это запуск консольной утилиты и перехват текста, который она выводит в консоль (stdout/stderr), писать собственную C++ библиотеку вообще не требуется. Эту задачу можно решить стандартными средствами 1С:Предприятия, которые гарантированно работают без прав администратора.

Проанализируем пример реализации с использованием перенаправления потоков ввода-вывода операционной системы:


// Функция запускает консольное приложение и возвращает его вывод в виде строки
Функция ПолучитьКонсольныйВыводПриложения(ПутьКПрограмме, ПараметрыЗапуска) Экспорт
    
    // Создаем уникальные имена файлов во временном каталоге пользователя
    ИмяВременногоФайлаВывода = ПолучитьИмяВременногоФайла("txt");
    ИмяФайлаСкрипта = ПолучитьИмяВременногоФайла("bat");
    
    // Формируем команду с перенаправлением вывода в текстовый файл
    // Символ ">" перенаправляет стандартный вывод (stdout)
    // Символы "2>&1" объединяют вывод ошибок (stderr) с основным потоком
    ТекстКоманды = """" + ПутьКПрограмме + """ " + ПараметрыЗапуска + " > """ + ИмяВременногоФайлаВывода + """ 2>&1";
    
    // Записываем команду в bat-файл для корректной обработки кириллицы и кавычек в путях
    ЗаписьТекста = Новый ЗаписьТекста(ИмяФайлаСкрипта, КодировкаТекста.OEM);
    ЗаписьТекста.ЗаписатьСтроку(ТекстКоманды);
    ЗаписьТекста.Закрыть();
    
    // Запускаем cmd-процесс в скрытом режиме и ожидаем завершения его работы
    ПараметрыЗапускаПриложения = Новый ОписаниеЗапускаПриложения;
    ПараметрыЗапускаПриложения.ТекущийКаталог = КаталогВременныхФайлов();
    ПараметрыЗапускаПриложения.ДождатьсяЗавершения = Истина;
    ПараметрыЗапускаПриложения.РежимЗапускаОкна = РежимЗапускаОкнаПриложения.Скрытое;
    
    ЗапуститьПриложение("cmd.exe /c """ + ИмяФайлаСкрипта + """", ПараметрыЗапускаПриложения);
    
    // Считываем результат работы утилиты
    ПотокЧтения = Новый ТекстовыйДокумент;
    ПотокЧтения.Прочитать(ИмяВременногоФайлаВывода, КодировкаТекста.OEM);
    РезультатВывода = ПотокЧтения.ПолучитьТекст();
    
    // Производим очистку временных файлов на диске
    Попытка
        УдалитьФайлы(ИмяФайлаСкрипта);
        УдалитьФайлы(ИмяВременногоФайлаВывода);
    Исключение
        // Игнорируем ошибки удаления, если файлы заблокированы ОС
    КонецПопытки;
    
    Возврат РезультатВывода;
    
КонецФункции

Данный подход является кроссплатформенным, не требует регистрации сторонних библиотек и полностью изолирован внутри временных папок пользователя, что позволяет обходить ограничения на запись в Program Files.

← На главную