В этой статье мы подробно разберем одну из самых неочевидных и сложных задач при разработке на платформе 1С: Предприятие — печать документов непосредственно с сервера. Мы проанализируем реальную ситуацию, когда мобильное приложение отправляет POST-запрос в 1С, и сервер должен обработать двоичные данные и распечатать PDF-файл (например, чек или этикетку) на сетевом принтере (в данном случае Brother QL-810W).
Мы рассмотрим, почему стандартные методы, работающие на клиенте, отказывают на сервере, разберем причины возникновения ошибок при использовании COM-объектов и предложим несколько проверенных архитектурных решений.
Многие разработчики сталкиваются с ситуацией, описанной автором: код, который идеально работает при запуске из клиентского приложения (Толстый или Тонкий клиент), перестает работать при переносе в серверный модуль, HTTP-сервис или регламентное задание.
Давайте проанализируем код, который использовал автор, пытаясь запустить Adobe Acrobat через COM-объект WScript.Shell:
ИмяПринтера = """Brother QL-810W""";
WshShell = Новый COMОбъект("WScript.Shell");
ПутьAdobeReader = WshShell.RegRead("HKCR\Software\Adobe\Acrobat\Exe\");
Shell = Новый COMОбъект("Shell.Application");
Shell.ShellExecute(ВремФайл, "", "", "print", 0);
Почему это не работает на сервере?
Сервер 1С:Предприятия запускается как служба Windows. Начиная с Windows Vista / Server 2008, службы работают в так называемой Сессии 0 (Session 0). Эта сессия полностью изолирована от пользовательского интерфейса.
Рассмотрим рабочие методы решения этой задачи, от простых к профессиональным.
Один из самых надежных способов, упомянутых в обсуждении, — отказ от печати непосредственно сервером 1С. Вместо этого мы используем схему с промежуточной очередью, задействовав конвейер обработки задач.
Как это работает:
ОчередьПечати. Если есть новые задания — она забирает их и печатает стандартным методом ТабличныйДокумент.Напечатать() или через локальные приложения.Плюсы метода:
Если архитектура требует печати именно с сервера без участия запущенных клиентских сеансов, нам необходимо использовать программное обеспечение, поддерживающее работу в режиме командной строки (CLI) без вызова графического интерфейса.
Важно: Adobe Acrobat Reader плохо подходит для серверной печати, так как он тяжеловесный и склонен "зависать" в памяти в сессии службы. Рассмотрим альтернативы.
В обсуждении справедливо заметили, что Foxit Reader стабильнее. Для печати можно использовать метод ЗапуститьПриложение. Обратите внимание, мы не используем COMОбъект, мы просто запускаем команду.
// Примерная команда для Foxit Reader
// /t
Команда = """C:\Path\To\FoxitReader.exe"" /t """ + ПутьКФайлуPDF + """ """ + ИмяПринтера + """";
ЗапуститьПриложение(Команда, , Истина); // Истина = ждать завершения
Это наиболее профессиональное решение для Windows-серверов. GhostScript — это интерпретатор PostScript и PDF, который не имеет GUI и идеально подходит для фоновых задач. Он позволяет растрировать PDF и отправлять его прямо в спулер печати Windows.
Пример кода 1С для запуска GhostScript:
// Путь к исполняемому файлу gswin64c.exe
ПутьGS = "C:\Program Files\gs\gs9.53.3\bin\gswin64c.exe";
// Параметры для печати на конкретный принтер Windows
// -sDEVICE=mswinpr2 — использование драйвера принтера Windows
// -sOutputFile="%printer%Имя_Принтера" — указание принтера
Параметры = " -dPrinted -dBATCH -dNOPAUSE -dNOSAFER -q -dNumCopies=1 -sDEVICE=mswinpr2 -sOutputFile=""%printer%" + ИмяПринтера + """ """ + ПутьКФайлуPDF + """";
ЗапуститьПриложение(ПутьGS + Параметры, , Истина);
Этот метод решает проблему "зависания" процессов, так как GS отрабатывает и сразу закрывается.
Автор темы предложил вариант с использованием VBScript, который "слушает" папку. Это рабочий вариант, если вы не хотите давать права пользователю USR1CV8 на принтеры.
Алгоритм действий:
G:\NEO\doc_print\).Давайте посмотрим на скрипт, приведенный автором, и улучшим понимание его работы. Скрипт использует Shell.Application для вызова команды контекстного меню "Print".
' VBScript для мониторинга папки
Set wssh = CreateObject("WScript.Shell")
Set objFS = CreateObject("Scripting.FileSystemObject")
Set objPath = objFS.GetFolder("G:\NEO\doc_print\")
Do
PrintDelInFolder objPath
WScript.Sleep 1000 ' Пауза 1 секунда
Loop
Sub PrintDelInFolder(objFolder)
For Each objItem In objFolder.Files
' Проверяем расширение PDF
If StrComp(objFS.GetExtensionName(objItem.Name), ".pdf", vbTextCompare) = 0 Then
' Отправка на печать через Shell.Execute (InvokeVerb "print")
Set objShell = CreateObject("Shell.Application")
' Важно: это сработает только если для PDF назначена программа по умолчанию, умеющая печатать
objShell.ShellExecute objItem.Path, "", "", "print", 0
' Пауза, чтобы файл успел уйти в спулер перед удалением
WScript.Sleep 5000
objFS.DeleteFile objItem.Path, true
End If
Next
End Sub
Недостаток этого метода: Трудно управлять выбором принтера. Команда print обычно отправляет файл на принтер, установленный в системе "по умолчанию". Для термопринтера Brother это может быть критично, если на сервере есть и другие принтеры.
В теме упоминается специфический принтер — Brother QL-810W. Это принтер этикеток. При печати PDF на таких устройствах часто возникают проблемы с масштабированием (драйвер пытается вписать лист А4 в размер этикетки 62мм).
Рекомендации для настройки:
ТабличныйДокумент имеет правильные поля и размер страницы, соответствующий размеру этикетки.В обсуждении также упоминался вариант использования отдельного сервера на Linux (Debian) с системой печати CUPS. Скрипт на сервере Linux принимает файл и выполняет команду lpr -P printer_name file.pdf. Это очень надежное решение для высоконагруженных систем ("фабрик печати"), но оно требует администрирования дополнительной ОС.
Для стабильной печати с сервера 1С рекомендуем: