Почему аварийно завершается работа 1С при присвоении значений из COM-объекта и как это исправить?

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

При организации интеграции между информационными базами 1С через внешнее COM-соединение разработчики нередко сталкиваются с ситуацией, когда стабильно работавший код после обновления платформы (например, при переходе на релизы ветки 8.3.9 или 8.3.10) начинает приводить к аварийному завершению работы платформы без генерации перехватываемых исключений.

Разберем типичный сценарий: обработка подключается к удаленной базе, выполняет запрос к регистру бухгалтерии, выгружает результат в таблицу значений и в цикле сопоставляет данные с табличной частью текущего документа или объекта.

Посмотрим на пример исходного кода запроса и цикла обработки данных:


Запрос.Текст = "ВЫБРАТЬ РАЗРЕШЕННЫЕ
               |    хр.Субконто1.Код КАК Код,
               |    СУММА(хр.КоличествоКонечныйРазвернутыйОстатокДт) КАК Количество
               |ИЗ
               |    РегистрБухгалтерии.Хозрасчетный.ОстаткиИОбороты(, &Дата, , ДвиженияИГраницыПериода, Счет = &Счет, , ) КАК хр
               |ГДЕ 
               |    хр.Субконто1.Код В(&НоменклатураКод) И хр.Субконто" + НомерТипаСубконто + ".Код = &СкладКод
               |СГРУППИРОВАТЬ ПО
               |    хр.Субконто1.Код";

Запрос.УстановитьПараметр("Дата", НачалоДня(Объект.Дата1));
Запрос.УстановитьПараметр("НоменклатураКод", МассивТоваровДляЗапроса);
Запрос.УстановитьПараметр("Счет", ББСч4101);
Запрос.УстановитьПараметр("СкладКод", Объект.СкладОтправитель.Код);
ОстаткиТовара = Запрос.Выполнить().Выгрузить();

Если ОстаткиТовара.Количество() = 0 Тогда
    Сообщить("Остатков указанного товара на складе-отправителе нет");
    Возврат;
КонецЕсли;

Для Каждого стр Из Объект.Товары Цикл
    Если стр.Количество = 0 Тогда
        Продолжить;
    КонецЕсли;
    НайдКод = ОстаткиТовара.Найти(стр.Код, "Код");
    Если НайдКод <> Неопределено Тогда
        Если стр.Количество > НайдКод.Количество Тогда
            стр.Количество = НайдКод.Количество;
        КонецЕсли;
    Иначе
        стр.Количество = 0;    
    КонецЕсли;
КонецЦикла;

На определенных итерациях цикла строка стр.Количество = НайдКод.Количество; вызывает внезапный вылет клиента или падение рабочего процесса без каких-либо видимых ошибок, несмотря на то что в отладчике значение поля Количество определяется как стандартное число.

Выясним причину сбоя при работе с COM-соединением

Проанализируем техническую сторону взаимодействия через внешнее соединение:

  1. Специфика маршалинга через интерфейс IDispatch: объект ТаблицаЗначений, полученный из внешнего соединения V83.COMConnector, не является стандартным локальным объектом текущей базы. Он представляет собой COM-обертку. Каждое обращение к полю строки (НайдКод.Количество) передается в виде низкоуровневой структуры VARIANT.
  2. Ошибки адресации памяти: при частых обращениях к свойствам COM-объекта в цикле и неявном преобразовании типов механизм маршалинга платформы в некоторых сборках дает сбой адресации памяти (Access Violation), что мгновенно завершает процесс 1cv8.exe или rphost.exe.
  3. Несоответствие зарегистрированной библиотеки: при установке новых релизов платформы в операционной системе может оставаться зарегистрированной библиотека comcntr.dll от старой версии, что приводит к несовместимости бинарных интерфейсов.

Способ 1. Явное приведение типов (гарантированное решение)

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

Изменим строку присвоения в цикле:


Если стр.Количество > НайдКод.Количество Тогда
    стр.Количество = Число(НайдКод.Количество);
КонецЕсли;

При вызове функции Число() платформа мгновенно извлекает значение из структуры VARIANT COM-объекта и формирует независимый локальный примитивный тип. Это полностью исключает конфликт маршалинга при записи в реквизит строки табличной части.

Способ 2. Повторная регистрация библиотеки comcntr.dll

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

Разберем по шагам порядок регистрации:

  1. Запустим командную строку cmd.exe от имени Администратора.
  2. Перейдем в каталог bin установленной версии платформы (например, C:\Program Files\1cv8\8.3.X.XXXX\bin\ для 64-разрядной версии или C:\Program Files (x86)\1cv8\8.3.X.XXXX\bin\ для 32-разрядной).
  3. Выполним команду регистрации компоненты: regsvr32 "comcntr.dll"
  4. Дождемся системного сообщения об успешной регистрации модуля.

Способ 3. Перегрузка данных в локальную таблицу значений

Чтобы избежать сотен и тысяч обращений через COM-интерфейс внутри цикла, рекомендуется один раз скопировать данные из COM-таблицы в локальную таблицу значений текущей базы перед началом обработки.

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


ЛокальныеОстатки = Новый ТаблицаЗначений;
ЛокальныеОстатки.Колонки.Добавить("Код", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(50)));
ЛокальныеОстатки.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));

Для Каждого СтрCOM Из ОстаткиТовара Цикл
    НоваяСтр = ЛокальныеОстатки.Добавить();
    НоваяСтр.Код = Строка(СтрCOM.Код);
    НоваяСтр.Количество = Число(СтрCOM.Количество);
КонецЦикла;

// Дальнейший поиск и сопоставление выполняем по полностью локальной таблице
Для Каждого стр Из Объект.Товары Цикл
    Если стр.Количество = 0 Тогда
        Продолжить;
    КонецЕсли;
    НайдКод = ЛокальныеОстатки.Найти(стр.Код, "Код");
    Если НайдКод <> Неопределено Тогда
        Если стр.Количество > НайдКод.Количество Тогда
            стр.Количество = НайдКод.Количество;
        КонецЕсли;
    Иначе
        стр.Количество = 0;    
    КонецЕсли;
КонецЦикла;

Важный вывод: при работе с любыми коллекциями и значениями, возвращаемыми через V83.COMConnector, всегда следует использовать явную типизацию примитивных типов (Число(), Строка(), Булево(), Дата()) либо сериализовать данные через форматы XML/JSON или REST API для обеспечения стабильности серверных процессов.

← На главную