При организации автоматического обмена данными между мобильными агентами и учетной системой 1С часто используется схема с промежуточной выгрузкой данных в формат DBF через внешнее COM-соединение (V83.COMConnector). В такой связке периодически возникает плавающая ошибка: часть выгруженных файлов оказывается с нарушенной структурой (меняются имена полей, типы колонок или их состав), хотя при ручном перезапуске процедуры файл формируется абсолютно корректно.
Разберем причины возникновения такого поведения платформы 1С:Предприятие 8.3 и рассмотрим надежные варианты решения проблемы.
Проанализируем технические факторы, которые приводят к возникновению «битых» файлов в автоматическом режиме:
V83.COMConnector операционная система и служба COM+ используют пул процессов (например, dllhost.exe). Байт-код скомпилированных модулей 1С сохраняется в оперативной памяти процесса. Если в конфигурацию вносились правки структуры DBF, активные COM-процессы могут продолжать исполнять старый код из памяти, формируя файл по устаревшему алгоритму.rphost и серверные контексты начинают выполнять разные версии одного и того же модуля.XBase выполняет запись напрямую на диск без механизмов транзакционности. При прерывании соединения или наложении блокировок заголовок DBF может остаться в поврежденном состоянии, а имена колонок длиннее 10 символов могут быть усечены платформой.Посмотрим на типовую ошибку: создание или очистка DBF-файла напрямую в целевой папке обмена. Чтобы полностью исключить повреждение структуры из-за конфликтов параллельного доступа и блокировок файловой системы, применим схему атомарного сохранения.
Сначала файл создается во временном каталоге с гарантированно уникальным именем, заполняется, закрывается объектом XBase, и только после этого перемещается в рабочую директорию одной операцией ПереместитьФайл().
Процедура ВыгрузитьЗаявкуВDBF(ПутьКПапкеОбмена, НомерДокумента, ДанныеТовары, Шапка) Экспорт
// Получаем уникальное имя во временном каталоге
ВременныйФайл = ПолучитьИмяВременногоФайла("dbf");
ДБФ = Новый XBase;
Если Константы.КодировкаДБФ.Получить() = Перечисления.КодировкиДБФ.ANSI Тогда
ДБФ.Кодировка = КодировкаXBase.ANSI;
Иначе
ДБФ.Кодировка = КодировкаXBase.OEM;
КонецЕсли;
// Формируем жесткую структуру полей (имена не более 10 символов)
ДБФ.Поля.Добавить("IDBUYER", "S", 100);
ДБФ.Поля.Добавить("IDFIRM", "S", 100);
ДБФ.Поля.Добавить("IDADDR", "S", 200);
ДБФ.Поля.Добавить("DELIVD", "D");
ДБФ.Поля.Добавить("DELIVS", "S", 10);
ДБФ.Поля.Добавить("DELIVPO", "S", 10);
ДБФ.Поля.Добавить("PRIM", "S", 200);
ДБФ.Поля.Добавить("IDGOODS", "S", 100);
ДБФ.Поля.Добавить("QUANT", "N", 10, 2);
ДБФ.Поля.Добавить("PRICE", "N", 10, 2);
ДБФ.Поля.Добавить("NOMZAK", "S", 30);
ДБФ.Поля.Добавить("DATEZAK", "D");
ДБФ.Поля.Добавить("SELLUNIT", "S", 100);
ДБФ.Поля.Добавить("DELIVERY", "S", 1);
ДБФ.Поля.Добавить("IDMEN", "S", 100);
ДБФ.Поля.Добавить("MTNOMKPK", "S", 100);
ДБФ.СоздатьФайл(ВременныйФайл);
// Заполнение строк таблицы
Для Каждого Стр Из ДанныеТовары Цикл
ДБФ.Добавить();
ДБФ.IDBUYER = Шапка.КлиентКод;
ДБФ.IDFIRM = Шапка.ФирмаКод;
ДБФ.IDADDR = Шапка.Адрес;
ДБФ.DELIVD = Шапка.ДатаДоставки;
ДБФ.PRIM = Шапка.Примечание;
ДБФ.IDGOODS = Стр.ТоварКод;
ДБФ.QUANT = Число(Стр.Количество);
ДБФ.PRICE = Число(Стр.Цена);
ДБФ.NOMZAK = НомерДокумента;
ДБФ.DATEZAK = ТекущаяДата();
ДБФ.SELLUNIT = Стр.ЕдиницаКод;
ДБФ.DELIVERY = ?(Шапка.Самовывоз, "1", "0");
ДБФ.IDMEN = Шапка.МенеджерКод;
ДБФ.MTNOMKPK = Шапка.УникальныйИдентификатор;
ДБФ.Записать();
КонецЦикла;
// Обязательно освобождаем файловый дескриптор
ДБФ.ЗакрытьФайл();
// Перемещение готового файла в целевую папку
ИтоговыйКаталог = ПутьКПапкеОбмена + "zak\" + Шапка.МенеджерКод + "\";
СоздатьКаталог(ИтоговыйКаталог);
ЦелевойФайл = ИтоговыйКаталог + "Z" + НомерДокумента + ".dbf";
// Если файл уже существует, удаляем его перед перемещением
Если НайтиФайлы(ЦелевойФайл).Количество() > 0 Тогда
УдалитьФайлы(ЦелевойФайл);
КонецЕсли;
ПереместитьФайл(ВременныйФайл, ЦелевойФайл);
КонецПроцедуры
Для гарантированного устранения проблем со старыми версиями модулей выполним регламентные действия по очистке кэша платформы:
dllhost.exe.1C:Enterprise 8.3 Server Agent (ragent) и очистим содержимое каталогов серверного кэша (каталоги вида snccntx* внутри директории srvinfo).%LOCALAPPDATA%\1C\1cv8 и %APPDATA%\1C\1cv8 для учетной записи, под которой запускаются фоновые задания и COM-сессии.Если в системе продолжают появляться файлы неизвестного происхождения, добавим логирование во внешний текстовый файл при каждом старте функции выгрузки. Это позволит определить точный идентификатор сеанса, имя пользователя и путь к исполняемой базе:
Процедура ЗафиксироватьСобытиеОбмена(ИмяФайла, КоличествоКолонок)
ТекстЛога = Новый ЗаписьТекста(
"C:\Logs\exchange_dbf.log",
КодировкаТекста.UTF8,
Символы.ПС,
Истина
);
СтрокаЛога = Строка(ТекущаяДата()) + " | Пользователь: " + ИмяПользователя()
+ " | Сеанс: " + НомерСеансаИнформационнойБазы()
+ " | Файл: " + ИмяФайла
+ " | Колонок: " + Строка(КоличествоКолонок);
ТекстЛога.ЗаписатьСтроку(СтрокаЛога);
ТекстЛога.Закрыть();
КонецПроцедуры
Такой аудит помогает быстро выявить, не обращается ли к каталогу обмена сторонний фоновый скрипт или забытая тестовая копия информационной базы.