При автоматизации документооборота в 1С разработчики часто сталкиваются с задачей организации потокового сканирования бумажных документов. Наиболее эффективным инструментом для этого является автоподатчик документов (ADF — Automatic Document Feeder), установленный на многофункциональном устройстве (МФУ). Однако при работе через внешние компоненты 1С, использующие интерфейс TWAIN, нередко возникают сбои: устройство физически прогоняет всю пачку бумаги, но в базу данных 1С загружается только первая страница, либо внешние события от компоненты вообще перестают поступать.
В этой статье мы подробно проанализируем причины возникновения подобных сбоев на примере популярного МФУ HP LaserJet Pro M426fdn, разберем базовый программный код интеграции компоненты TWAIN в конфигурациях 1С на обычных формах, а также рассмотрим альтернативные и более отказоустойчивые варианты реализации потокового сканирования.
Для начала проанализируем стандартную схему взаимодействия 1С с TWAIN-компонентой. Процесс сканирования состоит из двух основных этапов: инициализации (запуска сессии сканирования) и асинхронной обработки полученных изображений через механизм внешних событий платформы.
Рассмотрим пример кода инициализации сканирования. Этот метод запускает диалоговое окно сканера или инициирует процесс «в тихом режиме» без показа интерфейса драйвера:
// Настройка параметров сканирования
ПоказыватьДиалог = Истина;
РасширениеКартинки = "JPG";
Разрешение = 300;
Цветность = 1; // 1 - цветное, 0 - черно-белое, 2 - серое
Поворот = 90;
РазмерБумаги = 1; // А4
ПараметрСжатие = 10;
ДвустороннееСканирование = Ложь;
// Запуск процесса получения изображений
мКомпонентаTwain.НачатьСканирование(
ПоказыватьДиалог,
ИмяДрайвера, // Выбранное устройство в системе
РасширениеКартинки,
Разрешение,
Цветность,
Поворот,
РазмерБумаги,
ПараметрСжатие,
ДвустороннееСканирование
);
После вызова метода НачатьСканирование управление возвращается в 1С, а сам процесс получения картинок происходит асинхронно. Компонента TWAIN общается с драйвером физического устройства и при готовности каждого файла генерирует внешнее событие в платформе. Для обработки этих данных используется предопределенная процедура ВнешнееСобытие формы или модуля приложения:
Процедура ВнешнееСобытие(Источник, Событие, Данные)
// Проверяем, что событие пришло от нашей компоненты сканирования
Если Источник = "TWAIN" Тогда
Если Событие = "ImageAcquired" Тогда
// Событие срабатывает при успешном получении очередного листа
// В параметре "Данные" передается полный путь к временному файлу изображения
ВыбФайл = Новый Файл(Данные);
НоваяСтрока = ТаблицаСканов.Добавить();
НоваяСтрока.ИмяФайла = Формат(ТекущаяДата(), "ДФ=yyyyMMddHHmmss") + ВыбФайл.Расширение;
// Читаем файл во временное хранилище 1С
ДвоичныеДанные = Новый ДвоичныеДанные(Данные);
АдресКартинки = ПоместитьВоВременноеХранилище(ДвоичныеДанные, Новый УникальныйИдентификатор);
НоваяСтрока.АдресКартинки = АдресКартинки;
// Отображаем полученный скан на форме
ЭлементыФормы.ПолеКартинки.Картинка = Новый Картинка(ДвоичныеДанные);
// Удаляем временный файл с диска для очистки места
Попытка
УдалитьФайлы(Данные);
Исключение
// Обработка исключения, если файл занят процессом сканера
КонецПопытки;
ИначеЕсли Событие = "EndBatch" Тогда
// Данное событие сигнализирует об успешном окончании сканирования всей пачки документов
ПоказатьПредупреждение(, "Потоковое сканирование успешно завершено!");
ИначеЕсли Событие = "UserPressedCancel" Тогда
// Событие возникает, если пользователь вручную прервал процесс сканирования
ПоказатьПредупреждение(, "Сканирование было отменено пользователем.");
КонецЕсли;
КонецЕсли;
КонецПроцедуры
При корректной работе оборудования для пачки из трех листов в автоподатчике последовательность событий в 1С должна выглядеть следующим образом:
ImageAcquired (передан путь к файлу первого листа).ImageAcquired (передан путь к файлу второго листа).ImageAcquired (передан путь к файлу третьего листа).EndBatch (сканирование пакета документов завершено).Если же в процессе работы вы сталкиваетесь с тем, что сканер физически прогоняет все листы из лотка, но в процедуру ВнешнееСобытие попадает только первый файл (или события вообще отсутствуют), это свидетельствует о системном конфликте на уровне драйверов или программного интерфейса. Выясним основные причины этой неполадки и разберем методы их устранения.
Самой частой причиной сбоев при потоковом сканировании в операционных системах Windows является подмена реального TWAIN-драйвера программным эмулятором WIA (Windows Image Acquisition).
В операционной системе для одного физического устройства могут быть установлены несколько типов драйверов. В списке выбора источников сканирования они выглядят так:
Если внешняя компонента 1С обращается к устройству с префиксом WIA-, то передача пакета документов через автоподатчик нарушается. Эмулятор WIA некорректно обрабатывает асинхронные состояния передачи (State 6 и State 7 спецификации TWAIN). Сканер физически прогоняет всю пачку листов в буфер, но программная компонента генерирует событие ImageAcquired только один раз — для первого листа, после чего сессия аварийно завершается без отправки события EndBatch.
Решение: На компьютерах пользователей необходимо установить полноценный пакет программного обеспечения от производителя сканера (Full Software Solution), содержащий оригинальный промышленный TWAIN-драйвер. При выборе источника сканирования в настройках обработки 1С необходимо указывать только то устройство, имя которого не содержит приставки WIA-.
Еще одна типичная проблема: если в коде указать параметр ПоказыватьДиалог = Ложь, сканер упорно игнорирует лоток автоподатчика и пытается сканировать со стекла (из планшета), даже если бумага загружена в верхний отсек МФУ.
Это происходит потому, что в стандартном методе НачатьСканирование() большинства устаревших компонент (например, TWAINAddIn версии 1.x) отсутствует параметр принудительного выбора источника бумаги. В скрытом режиме компонента запрашивает у драйвера параметры по умолчанию, установленные в самой операционной системе. У большинства МФУ по умолчанию приоритетным источником выставлен планшет (Flatbed).
Для решения этой проблемы без изменения программного кода выполним следующие действия по шагам:
Если используется современная версия платформы 1С и БСП (Библиотеки стандартных подсистем), рекомендуется обновить внешнюю компоненту сканирования до версии 2.х. В новых версиях реализована возможность передачи расширенной структуры настроек в формате XML/JSON, где можно явно передавать параметр выбора источника бумаги программно.
Если работа с TWAIN-компонентой в вашей конфигурации нестабильна или вызывает регулярные падения платформы, рассмотрим альтернативный вариант — прямое управление сканированием средствами операционной системы через COM-объект WIA.DeviceManager.
Для реализации потокового сканирования с автоподатчика через WIA крайне важно правильно настроить системные свойства устройства. Обычный метод ShowAcquireImage() для этой задачи не подходит, так как он рассчитан на получение только одной картинки. Нам потребуется программно инициализировать устройство, проверить наличие бумаги в лотке автоподатчика и запустить цикл сканирования.
Проанализируем пример кода для работы с WIA:
// Инициализация менеджера устройств WIA
Попытка
МенеджерУстройств = Новый COMОбъект("WIA.DeviceManager");
Исключение
ВызватьИсключение "Библиотека WIA не зарегистрирована в системе!";
КонецПопытки;
// Выбор сканера пользователем через стандартный диалог Windows
Попытка
ДиалогВыбора = МенеджерУстройств.SelectDevice(1, Истина); // 1 - тип устройства: Сканер
Исключение
// Пользователь отменил выбор или сканеры не найдены
Возврат;
КонецПопытки;
// Настройка параметров устройства для работы с ADF
// Свойство 3088 (WIA_DPS_DOCUMENT_HANDLING_SELECT) определяет источник бумаги:
// 1 - Автоподатчик (Feeder)
// 2 - Планшет (Flatbed)
// 5 - Двусторонний режим автоподатчика (Duplex)
Попытка
ДиалогВыбора.Properties("3088").SetValue(1);
Исключение
Предупреждение("Данное устройство не поддерживает автоподатчик документов!");
Возврат;
КонецПопытки;
// Начинаем цикл потокового сканирования пачки документов
ИндексЛиста = 0;
УспешноЗавершено = Ложь;
Пока Истина Цикл
// Проверяем свойство 3087 (WIA_DPS_DOCUMENT_HANDLING_STATUS) на наличие бумаги в ADF:
// Значение 1 означает, что в лотке есть документы для сканирования
Попытка
СтатусБумаги = ДиалогВыбора.Properties("3087").Value;
Если (СтатусБумаги И 1) = 0 Тогда
// Бумага в лотке закончилась
Прервать;
КонецЕсли;
Исключение
// Если свойство не поддерживается драйвером, продолжаем сканирование до возникновения ошибки
КонецПопытки;
Попытка
// Выполняем захват изображения с первого доступного элемента (Items(1))
ЭлементУстройства = ДиалогВыбора.Items(1);
ИзображениеWIA = ЭлементУстройства.Transfer();
// Сохраняем полученный лист во временный файл JPG
ИндексЛиста = ИндексЛиста + 1;
ПутьКФайлу = КаталогВременныхФайлов() + "scan_wia_" + Формат(ИндексЛиста, "ЧГ=0") + ".jpg";
// Удаляем файл, если он уже существовал
ФайлПроверки = Новый Файл(ПутьКФайлу);
Если ФайлПроверки.Существует() Тогда
УдалитьФайлы(ПутьКФайлу);
КонецЕсли;
// Для конвертации и сохранения используем объект WIA.ImageProcess
ПроцессорИзображений = Новый COMОбъект("WIA.ImageProcess");
ПроцессорИзображений.Filters.Add(ПроцессорИзображений.FilterInfos("Convert").FilterID);
ПроцессорИзображений.Filters(1).Properties("FormatID").Value = "{B9633895-A75E-4D57-BF83-8A755567C25D}"; // GUID формата JPEG
ИзображениеWIA = ПроцессорИзображений.Apply(ИзображениеWIA);
ИзображениеWIA.SaveFile(ПутьКФайлу);
// Здесь можно реализовать код загрузки сохраненного файла в информационную базу 1С...
Исключение
// При окончании бумаги в автоподатчике WIA генерирует системное исключение
// Код ошибки отсутствия бумаги: 0x80210003
ИнформацияОбОшибке = ОписаниеОшибки();
Если Найти(ИнформацияОбОшибке, "0x80210003") > 0 Тогда
// Бумага успешно закончилась, выходим из цикла без вывода сообщения об ошибке
Прервать;
Иначе
Сообщить("Ошибка при сканировании листа " + ИндексЛиста + ": " + ИнформацияОбОшибке, СтатусСообщения.Важное);
Прервать;
КонецЕсли;
КонецПопытки;
КонецЦикла;
В крупных распределенных системах, а также при работе пользователей в терминальных сессиях (RDP) стандартные методы сканирования (TWAIN/WIA) часто работают нестабильно из-за проблем проброса портов и разрядности платформы 1С (например, 32-битная компонента TWAIN не может инициализироваться в 64-битной платформе 1С).
Для обхода этих ограничений разработчики используют внешние утилиты сканирования с открытым исходным кодом. Самым надежным решением является утилита NAPS2 (Not Another PDF Scanner 2), которая имеет консольную версию.
Рассмотрим схему реализации потокового сканирования через NAPS2 по шагам:
HP_Autofeeder. В профиле жестко задаются все параметры устройства: использование автоподатчика, разрешение 300 dpi, формат бумаги, двусторонний режим.naps2.console.exe с параметрами командной строки. Программа выполняет сканирование всей пачки документов в автономном режиме и сохраняет готовые файлы в указанную папку на диске.Посмотрим на пример кода запуска консоли NAPS2 из 1С:
// Определение путей и параметров запуска
КаталогНазначения = КаталогВременныхФайлов() + "NAPS_Scans\";
СоздатьКаталог(КаталогНазначения);
// Очищаем каталог назначения перед запуском нового процесса
МассивФайлов = НайтиФайлы(КаталогНазначения, "*.*");
Для Каждого ФайлИзКаталога Из МассивФайлов Цикл
УдалитьФайлы(ФайлИзКаталога.ПолноеИмя);
КонецЦикла;
ПутьККонсоли = "C:\Program Files (x86)\NAPS2\naps2.console.exe";
ПрофильСканирования = "HP_Autofeeder";
ШаблонИменФайлов = КаталогНазначения + "page_*.jpg";
// Формируем строку запуска командной строки:
// -p: имя профиля NAPS2
// -o: путь для сохранения файлов
// --multiple: сохранять каждую страницу в отдельный файл
// --quiet: не показывать интерфейс программы
ПараметрыЗапуска = СтрШаблон("-p ""%1"" -o ""%2"" --multiple --quiet", ПрофильСканирования, ШаблонИменФайлов);
// Запускаем процесс сканирования
Попытка
// Третий параметр "Истина" означает, что 1С будет ожидать завершения процесса сканирования
КодВозврата = Неопределено;
ЗапуститьПриложение(ПутьККонсоли + " " + ПараметрыЗапуска, , Истина, КодВозврата);
Если КодВозврата = 0 Тогда
// Процесс сканирования успешно завершен, обрабатываем полученные файлы
ОбработатьСканированныеФайлы(КаталогНазначения);
Иначе
ВызватьИсключение "Ошибка выполнения сканирования утилитой NAPS2. Код возврата: " + КодВозврата;
КонецЕсли;
Исключение
Сообщить("Не удалось запустить утилиту сканирования: " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
Метод обработки полученных файлов в целевой папке выглядит следующим образом:
Процедура ОбработатьСканированныеФайлы(ПутьКПапке)
НайденныеФайлы = НайтиФайлы(ПутьКПапке, "*.jpg");
Если НайденныеФайлы.Количество() = 0 Тогда
ПоказатьПредупреждение(, "В лотке автоподатчика отсутствовала бумага или сканирование не состоялось.");
Возврат;
КонецЕсли;
Для Каждого ФайлСкана Из НайденныеФайлы Цикл
// Читаем файл в двоичные данные
ДвоичныеДанные = Новый ДвоичныеДанные(ФайлСкана.ПолноеИмя);
// Помещаем скан в табличную часть или прикрепляем к документу базы данных
НоваяСтрока = ТаблицаСканированныхДокументов.Добавить();
НоваяСтрока.ИмяФайла = ФайлСкана.Имя;
НоваяСтрока.ХранилищеФайла = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));
// Удаляем обработанный файл с диска
Попытка
УдалитьФайлы(ФайлСкана.ПолноеИмя);
Исключение
// Обработка блокировки файла операционной системой
КонецПопытки;
КонецЦикла;
Сообщить("Успешно импортировано документов: " + НайденныеФайлы.Количество());
КонецПроцедуры
Для крупных современных офисов с сетевыми МФУ (такими как HP LaserJet Pro M426fdn, где символ "n" означает наличие сетевого интерфейса Ethernet) самым надежным и производительным решением является полный отказ от использования драйверов сканирования на рабочих местах пользователей.
Рассмотрим по шагам архитектуру организации сетевого сканирования:
\\FileServer\Scans\), к которому предоставляются права на запись для МФУ и права на чтение/запись для службы сервера 1С.\\FileServer\Scans\ на наличие новых файлов.Этот подход полностью снимает проблемы сбоев локальных TWAIN/WIA драйверов, исключает необходимость установки стороннего ПО на компьютеры пользователей, не нагружает терминальные сессии RDP и сводит к минимуму время работы сотрудников с оргтехникой.