При организации интеграции между информационными базами 1С через внешнее COM-соединение разработчики нередко сталкиваются с ситуацией, когда стабильно работавший код после обновления платформы (например, при переходе на релизы ветки 8.3.9 или 8.3.10) начинает приводить к аварийному завершению работы платформы без генерации перехватываемых исключений.
Разберем типичный сценарий: обработка подключается к удаленной базе, выполняет запрос к регистру бухгалтерии, выгружает результат в таблицу значений и в цикле сопоставляет данные с табличной частью текущего документа или объекта.
Посмотрим на пример исходного кода запроса и цикла обработки данных:
Запрос.Текст = "ВЫБРАТЬ РАЗРЕШЕННЫЕ
| хр.Субконто1.Код КАК Код,
| СУММА(хр.КоличествоКонечныйРазвернутыйОстатокДт) КАК Количество
|ИЗ
| РегистрБухгалтерии.Хозрасчетный.ОстаткиИОбороты(, &Дата, , ДвиженияИГраницыПериода, Счет = &Счет, , ) КАК хр
|ГДЕ
| хр.Субконто1.Код В(&НоменклатураКод) И хр.Субконто" + НомерТипаСубконто + ".Код = &СкладКод
|СГРУППИРОВАТЬ ПО
| хр.Субконто1.Код";
Запрос.УстановитьПараметр("Дата", НачалоДня(Объект.Дата1));
Запрос.УстановитьПараметр("НоменклатураКод", МассивТоваровДляЗапроса);
Запрос.УстановитьПараметр("Счет", ББСч4101);
Запрос.УстановитьПараметр("СкладКод", Объект.СкладОтправитель.Код);
ОстаткиТовара = Запрос.Выполнить().Выгрузить();
Если ОстаткиТовара.Количество() = 0 Тогда
Сообщить("Остатков указанного товара на складе-отправителе нет");
Возврат;
КонецЕсли;
Для Каждого стр Из Объект.Товары Цикл
Если стр.Количество = 0 Тогда
Продолжить;
КонецЕсли;
НайдКод = ОстаткиТовара.Найти(стр.Код, "Код");
Если НайдКод <> Неопределено Тогда
Если стр.Количество > НайдКод.Количество Тогда
стр.Количество = НайдКод.Количество;
КонецЕсли;
Иначе
стр.Количество = 0;
КонецЕсли;
КонецЦикла;
На определенных итерациях цикла строка стр.Количество = НайдКод.Количество; вызывает внезапный вылет клиента или падение рабочего процесса без каких-либо видимых ошибок, несмотря на то что в отладчике значение поля Количество определяется как стандартное число.
Проанализируем техническую сторону взаимодействия через внешнее соединение:
ТаблицаЗначений, полученный из внешнего соединения V83.COMConnector, не является стандартным локальным объектом текущей базы. Он представляет собой COM-обертку. Каждое обращение к полю строки (НайдКод.Количество) передается в виде низкоуровневой структуры VARIANT.1cv8.exe или rphost.exe.comcntr.dll от старой версии, что приводит к несовместимости бинарных интерфейсов.Для того чтобы разорвать зависимость от внешней COM-структуры и принудительно создать локальную переменную в оперативной памяти текущего сеанса, применим явное приведение типов при помощи встроенной функции Число().
Изменим строку присвоения в цикле:
Если стр.Количество > НайдКод.Количество Тогда
стр.Количество = Число(НайдКод.Количество);
КонецЕсли;
При вызове функции Число() платформа мгновенно извлекает значение из структуры VARIANT COM-объекта и формирует независимый локальный примитивный тип. Это полностью исключает конфликт маршалинга при записи в реквизит строки табличной части.
Если обновление платформы прошло некорректно или на сервере установлено несколько версий 1С:Предприятия, необходимо зарегистрировать актуальную библиотеку COM-соединения вручную.
Разберем по шагам порядок регистрации:
cmd.exe от имени Администратора.bin установленной версии платформы (например, C:\Program Files\1cv8\8.3.X.XXXX\bin\ для 64-разрядной версии или C:\Program Files (x86)\1cv8\8.3.X.XXXX\bin\ для 32-разрядной).
regsvr32 "comcntr.dll"
Чтобы избежать сотен и тысяч обращений через COM-интерфейс внутри цикла, рекомендуется один раз скопировать данные из COM-таблицы в локальную таблицу значений текущей базы перед началом обработки.
Рассмотрим пример создания типизированной локальной таблицы:
ЛокальныеОстатки = Новый ТаблицаЗначений;
ЛокальныеОстатки.Колонки.Добавить("Код", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(50)));
ЛокальныеОстатки.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
Для Каждого СтрCOM Из ОстаткиТовара Цикл
НоваяСтр = ЛокальныеОстатки.Добавить();
НоваяСтр.Код = Строка(СтрCOM.Код);
НоваяСтр.Количество = Число(СтрCOM.Количество);
КонецЦикла;
// Дальнейший поиск и сопоставление выполняем по полностью локальной таблице
Для Каждого стр Из Объект.Товары Цикл
Если стр.Количество = 0 Тогда
Продолжить;
КонецЕсли;
НайдКод = ЛокальныеОстатки.Найти(стр.Код, "Код");
Если НайдКод <> Неопределено Тогда
Если стр.Количество > НайдКод.Количество Тогда
стр.Количество = НайдКод.Количество;
КонецЕсли;
Иначе
стр.Количество = 0;
КонецЕсли;
КонецЦикла;
Важный вывод: при работе с любыми коллекциями и значениями, возвращаемыми через V83.COMConnector, всегда следует использовать явную типизацию примитивных типов (Число(), Строка(), Булево(), Дата()) либо сериализовать данные через форматы XML/JSON или REST API для обеспечения стабильности серверных процессов.