Разработчики, работающие с платформой 1С 7.7, часто сталкиваются с необходимостью печати внешних файлов, таких как PDF. Стандартный подход с использованием объекта shell.application и метода ShellExecute через глагол print кажется самым простым, но он имеет существенный недостаток: файл отправляется на принтер, который установлен в операционной системе по умолчанию. Если же нам требуется отправить документ на конкретный аппарат (например, на складской принтер этикеток или бухгалтерский сетевой принтер), стандартных параметров ShellExecute оказывается недостаточно.
В этой статье мы подробно разберем, как обойти это ограничение, проанализируем различные методики — от манипуляций с системными настройками до использования внешних специализированных утилит.
Рассмотрим типовой код, который часто используют в 1С 7.7:
ПечПДФ = СоздатьОбъект("shell.application");
ПечПДФ.ShellExecute(ФайлДляПечати, "", "", "print", 1);
Функция ShellExecute вызывает системную команду, связанную с расширением файла. Для PDF это обычно Adobe Reader или Foxit Reader. Глагол print в системном реестре Windows жестко прописан для использования принтера по умолчанию. Параметра для передачи имени принтера в этом методе просто не существует. Выясним, какие у нас есть альтернативы.
Проанализируем самый доступный способ, не требующий установки дополнительных программ. Мы можем программно изменить принтер по умолчанию в Windows прямо перед печатью, отправить файл на выполнение и затем вернуть настройки обратно. Для этого идеально подходит объект WScript.Network.
Разберем реализацию этого метода по шагам:
ShellExecute.
// Создаем объект для управления системными настройками
Сеть = СоздатьОбъект("WScript.Network");
// Задаем имя нужного принтера (точно так, как он называется в Windows)
НужныйПринтер = "\\Server\HP_LaserJet_400";
Попытка
// Устанавливаем принтер по умолчанию
Сеть.SetDefaultPrinter(НужныйПринтер);
// Печатаем файл
ПечПДФ = СоздатьОбъект("shell.application");
ПечПДФ.ShellExecute(ФайлДляПечати, "", "", "print", 1);
// Важно: иногда приложению нужно время, чтобы подхватить принтер
// Здесь можно добавить небольшую паузу
Исключение
Сообщить("Ошибка при смене принтера: " + ОписаниеОшибки());
КонецПопытки;
Если мы знаем, какая программа установлена для просмотра PDF, мы можем вызвать её напрямую с параметрами командной строки. Это более надежный метод, так как он не затрагивает глобальные настройки системы. Большинство популярных программ поддерживают ключи печати на конкретное устройство.
Рассмотрим пример для Adobe Reader. У него есть ключ /t, который принимает путь к файлу и имя принтера.
// Синтаксис: AcroRd32.exe /t "Путь_к_файлу" "Имя_принтера" "Имя_драйвера" "Порт"
// Драйвер и порт в современных ОС часто можно игнорировать
ПутьКРидеру = "C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\AcroRd32.exe";
ИмяПринтера = "HP LaserJet 400";
Команда = """" + ПутьКРидеру + """ /t """ + ФайлДляПечати + """ """ + ИмяПринтера + """";
// Выполняем команду
КомандаСистемы(Команда);
Для Foxit Reader синтаксис еще проще:
FoxitReader.exe /p "Файл.pdf" "Имя_принтера"
Посмотрим на ситуацию со стороны стабильности. Установка тяжелых ридеров (Adobe) на сервер или рабочие станции может быть избыточной. Профессионалы часто используют SumatraPDF — это крохотный бесплатный просмотрщик, который можно просто положить в папку с базой 1С (файл SumatraPDF.exe).
Выясним преимущества этого метода:
Пример кода для 1С 7.7:
ПутьКПрограмме = КаталогИБ() + "ExtForms\SumatraPDF.exe";
ФайлPDF = "C:\Docs\Report.pdf";
Принтер = "Canon_LBP6000";
// Формируем строку запуска
// Ключ -print-to позволяет указать целевой принтер
Команда = """" + ПутьКПрограмме + """ -print-to """ + Принтер + """ """ + ФайлPDF + """";
// Запускаем в скрытом режиме
WshShell = СоздатьОбъект("WScript.Shell");
WshShell.Run(Команда, 0, 1); // 0 - скрыть окно, 1 - ждать завершения
В ходе обсуждения на форуме был предложен вариант использования MSScriptControl для работы с коллекцией принтеров через Win32_Printer. Это полезно, когда нам нужно не просто отправить на печать, а сначала убедиться, что принтер существует или имеет определенные атрибуты.
Проанализируем фрагмент кода, который определяет принтер по умолчанию с помощью побитовых операций над атрибутами:
Скрипт = СоздатьОбъект("MSScriptControl.ScriptControl");
Скрипт.Language = "vbscript";
Скрипт.AddCode("
|Function IsDefault(Attributes)
| IsDefault = (Attributes AND 4)
|End Function");
Локатор = СоздатьОбъект("wbemscripting.swbemlocator");
Сервис = Локатор.ConnectServer();
Принтеры = Сервис.ExecQuery("Select * from Win32_Printer");
// Перебор коллекции принтеров
Перечисление = Принтеры._NewEnum;
Пока Перечисление.MoveNext(Принтер) = -1 Цикл
Если Скрипт.Run("IsDefault", Принтер.Attributes) <> 0 Тогда
Сообщить("Текущий принтер по умолчанию: " + Принтер.Name);
КонецЕсли;
КонецЦикла;
Условие Attributes AND 4 (или Attributes % 8 / 4 = 1 в JavaScript-версии) проверяет четвертый бит атрибутов объекта Win32_Printer, который в спецификации WMI как раз и отвечает за признак PRINTER_ATTRIBUTE_DEFAULT.
Существует еще более узконаправленное решение — утилита PDFtoPrinter. В отличие от ридеров, она вообще не имеет графического интерфейса и создана исключительно для одной задачи: передать PDF в очередь печати конкретного устройства.
Разберем пример использования:
Команда = "PDFtoPrinter.exe ""документ.pdf"" ""Имя Принтера""";
КомандаСистемы(Команда);
Это считается одним из самых «чистых» способов автоматизации, так как он исключает появление лишних окон на экране пользователя и работает максимально прозрачно.
Проанализируем типичную ошибку при указании имени принтера. Если принтер сетевой, его имя в системе может выглядеть как \\PRN-SERVER\Buh_Laser. При передаче такого имени в командную строку через 1С необходимо соблюдать следующие правила:
Подводя итог, можно сказать: если вы хотите решить задачу без установки внешних компонент (ВК), оптимальным выбором будет либо временная смена принтера через WScript.Network, либо использование портативной утилиты вроде SumatraPDF, которую можно распространять вместе с конфигурацией.