При интеграции информационных баз 1С через COM-соединение (V83.COMConnector) разработчики часто сталкиваются с неочевидными ошибками. Одна из самых коварных ситуаций — когда код отлично работает в режиме Предприятия (Тонкий/Толстый клиент), но падает с ошибкой при вызове через внешнее соединение, даже если визуально все необходимые галочки в свойствах модулей установлены. Рассмотрим подробно проблему, когда при вызове экспортной функции общего модуля возникает ошибка обращения к внутренней (неэкспортной) функции, и разберем, почему расширение конфигурации может не помогать в "лоб".
Представим ситуацию: у нас есть внешняя обработка или скрипт, который подключается к базе 1С через COM-соединение для создания документов. В процессе работы вызывается экспортная процедура из общего модуля.
Структура общего модуля в основной конфигурации выглядит следующим образом:
// Общий модуль: РаботаСДокументами (Галочки: Сервер, Внешнее соединение)
Функция СоздатьДокумент() Экспорт
// Вызов внутренней служебной функции
Возврат ПолучитьДанныеДляДокумента();
КонецФункции
&НаСервере
Функция ПолучитьДанныеДляДокумента()
// Логика получения данных
Возврат Результат;
КонецФункции
При попытке выполнить метод СоздатьДокумент() через COM-соединение платформа возвращает ошибку:
Ошибка: Процедура или функция с указанным именем не определена (ПолучитьДанныеДляДокумента)
Разработчик пытается исправить ситуацию, создав расширение конфигурации и переопределив методы, но ошибка сохраняется. Давайте разберем, почему это происходит и как это исправить.
Чтобы понять причину ошибки, нам нужно вспомнить, как платформа компилирует модули в разных режимах запуска.
V83.COMConnector. Для платформы это режим "Внешнее соединение".&НаСервере, &НаКлиенте и т.д.В нашем примере у внутренней функции ПолучитьДанныеДляДокумента стоит директива &НаСервере.
В чем подвох?
Директива &НаСервере указывает платформе, что данный метод должен быть скомпилирован и доступен в контексте сервера (когда работает сервер 1С предприятия для тонкого/толстого клиента). Однако контекст Внешнего соединения отличается от контекста Сервера.
Когда платформа выполняет синтаксический контроль модуля в режиме внешнего соединения, она видит директиву &НаСервере и, в зависимости от настроек платформы и версии, может "вырезать" этот метод из компилируемого образа модуля для внешнего соединения, так как считает, что этот метод предназначен только для классического серверного вызова.
В итоге получается ситуация:
1. Экспортная функция СоздатьДокумент компилируется (так как она "общая" и доступна в контексте модуля).
2. Внутренняя функция ПолучитьДанныеДляДокумента исключается из контекста из-за директивы &НаСервере.
3. При попытке вызова первая функция не находит вторую. Происходит ошибка компиляции модуля или ошибка времени выполнения.
Частая попытка решения — сделать расширение и переопределить методы через аннотацию &Вместо, добавив Экспорт или убрав директивы.
Однако, если основной модуль конфигурации не проходит синтаксический контроль в контексте текущего соединения (в нашем случае COM), платформа может вообще не загрузить этот модуль или не применить к нему расширение корректно. Платформа сначала пытается "собрать" основной модуль. Если в нем есть логическая дыра (вызов несуществующей для данного контекста функции), то расширение, которое пытается перехватить этот вызов, просто не сработает, потому что базовый код уже "сломан".
Самый надежный способ решить проблему, не меняя глобально логику работы конфигурации — использовать инструкции препроцессора. Нам нужно явно указать платформе, какой код должен выполняться в режиме внешнего соединения.
Если вы не можете менять основную конфигурацию, но можете использовать расширение, вам нужно изолировать вызов проблемной функции.
В расширении:
Мы можем перехватить вызов экспортной функции и написать свою реализацию, которая будет работать корректно в COM-соединении.
&Вместо("СоздатьДокумент")
Функция Расш_СоздатьДокумент() Экспорт
#Если ВнешнееСоединение Тогда
// Здесь пишем код специально для COM, избегая вызова
// функций с директивой &НаСервере, если они недоступны.
// Либо дублируем логику получения данных.
Возврат СвояФункцияПолученияДанных();
#Иначе
// В обычном режиме продолжаем вызывать стандартный метод
Возврат ПродолжитьВызов();
#КонецЕсли
КонецФункции
Если у вас есть возможность вносить изменения в основную конфигурацию (или вы разрабатываете её сами), то правильным подходом будет корректная расстановка директив.
Для методов, которые должны работать и на Сервере (в сеансе пользователя), и во Внешнем соединении (COM), не следует ставить директиву &НаСервере, если у самого общего модуля уже стоят галочки "Сервер" и "Внешнее соединение". Методы без директив внутри такого модуля автоматически становятся доступными во всех контекстах, отмеченных в свойствах модуля.
Правильный код общего модуля:
// Свойства модуля: [x] Сервер, [x] Внешнее соединение
Функция СоздатьДокумент() Экспорт
Возврат ПолучитьДанныеДляДокумента();
КонецФункции
// Убираем директиву &НаСервере, так как она ограничивает видимость
// Функция теперь доступна и на Сервере, и во Внешнем соединении
Функция ПолучитьДанныеДляДокумента()
Возврат Результат;
КонецФункции
Иногда проблема кроется не в коде, а в правах расширения. Если расширение подключено, но методы не подменяются в COM-соединении, проверьте флаг Безопасный режим.
В безопасном режиме расширениям часто запрещено выполнение серверного кода или переопределение определенных методов, что критично для COM-соединения.
Подведем итог, как избежать подобных проблем в будущем:
ExternalConnection отличается от контекста Server.&НаСервере внутри общих модулей, у которых установлена галочка "Внешнее соединение", для функций, которые должны вызываться через COM. Оставляйте такие функции без аннотаций.#Если ВнешнееСоединение Тогда, чтобы отделить логику интеграции от логики интерактивной работы.