Перечень установленных принтеров в 1С 8.3: COM, WMI, реестр и управляемые формы
Содержание
Получить список принтеров в 1С проще всего через COM-объект Windows: Новый COMОбъект("WScript.Network").EnumPrinterConnections() отдаёт имена всех подключённых принтеров, а WMI-запрос к классу Win32_Printer добавляет драйвер, порт, признак принтера по умолчанию и состояние. Код выполняется на клиенте под Windows. В веб-клиенте COM недоступен.
Задача общая для платформы 1С:Предприятие 8.3 и не зависит от конфигурации: код одинаково работает во внешней обработке, в расширении и в собственной форме Бухгалтерии, ЗУП или УТ. Нужен он, когда документы печатаются без диалога на заранее выбранный принтер: этикетки, чеки склада, пачки накладных.
Что 1С умеет с принтерами сама, без COM
Встроенный язык работает с принтером через табличный документ. У объекта ТабличныйДокумент есть свойство ИмяПринтера и метод Напечатать(). Платформа берёт список принтеров из Windows и показывает его в стандартном диалоге печати.
Отдельной функции, которая вернула бы перечень принтеров массивом, в привычных релизах платформы нет. Поэтому разработчики берут список у операционной системы. Если у вас свежая платформа, наберите слово «принтер» в поиске синтакс-помощника: так вы сразу увидите, что доступно именно в вашем релизе.
Что даёт платформа без внешних объектов:
- 🖨️ диалог печати со списком всех принтеров пользователя;
- 📝 свойство
ИмяПринтера— куда печатать, если диалог не показывается; - ⚙️ свойства копий, ориентации и размера страницы у табличного документа;
- 🔁 печать без диалога через
Напечатать(РежимИспользованияДиалогаПечати.НеИспользовать).
Получается, что выбрать принтер пользователь может сам в диалоге, а программно перечислить принтеры — только через Windows. Ниже три способа это сделать.
| Способ | Что возвращает | Когда брать | Ограничения |
|---|---|---|---|
| WScript.Network | имя и порт | нужен только список имён | нет драйвера и статуса |
WMI, Win32_Printer | имя, драйвер, порт, «по умолчанию», офлайн | нужна фильтрация и подробности | служба WMI должна работать |
Реестр, reg query, PowerShell | имена из настроек пользователя | COM запрещён политикой | разбор текста, кодировка |
Список принтеров через объект WScript.Network
Объект WScript.Network входит в Windows Script Host и есть в любой современной Windows. Метод EnumPrinterConnections() возвращает коллекцию, где элементы идут парами: сначала порт, затем имя принтера. Поэтому имена лежат на нечётных позициях: 1, 3, 5 и так далее.
&НаКлиенте
Функция СписокПринтеровWSH()
Результат = Новый Массив;
#Если НЕ ВебКлиент Тогда
Сеть = Новый COMОбъект("WScript.Network");
Подключения = Сеть.EnumPrinterConnections();
// элементы идут парами: [0] — порт, [1] — имя принтера
Для Индекс = 0 По Подключения.Count() / 2 - 1 Цикл
Результат.Добавить(Подключения.Item(Индекс * 2 + 1));
КонецЦикла;
#КонецЕсли
Возврат Результат;
КонецФункции
Проверим индексы на примере. В системе три принтера, Count() вернёт 6. Цикл пройдёт значения 0, 1, 2 и возьмёт элементы 1, 3 и 5 — ровно три имени.
У этого объекта есть ещё два полезных метода:
SetDefaultPrinter("Имя")— назначает принтер по умолчанию для текущего пользователя Windows.AddWindowsPrinterConnection("\\Сервер\Принтер")— подключает сетевой принтер.
Смена принтера по умолчанию действует на все программы пользователя, не только на 1С. Делайте это только с его согласия.
Как получить список принтеров через WMI-запрос
WMI даёт гораздо больше, чем имя. Класс Win32_Printer хранит драйвер, порт, сетевой ли принтер, отключён ли он и выбран ли по умолчанию. Запрос пишется на языке WQL, он похож на SQL.
&НаКлиенте
Функция ТаблицаПринтеровWMI()
Результат = Новый Массив;
#Если НЕ ВебКлиент Тогда
WMI = ПолучитьCOMОбъект("winmgmts:\\.\root\cimv2");
Выборка = WMI.ExecQuery("SELECT Name, DriverName, PortName, Default, Network, WorkOffline FROM Win32_Printer");
Для Каждого Принтер Из Выборка Цикл
Описание = Новый Структура;
Описание.Вставить("Имя", Принтер.Name);
Описание.Вставить("Драйвер", Принтер.DriverName);
Описание.Вставить("Порт", Принтер.PortName);
Описание.Вставить("ПоУмолчанию", Принтер.Default);
Описание.Вставить("Сетевой", Принтер.Network);
Описание.Вставить("Отключен", Принтер.WorkOffline);
Результат.Добавить(Описание);
КонецЦикла;
#КонецЕсли
Возврат Результат;
КонецФункции
Фильтр можно поставить прямо в запрос. Несколько готовых условий:
- 📌 только принтер по умолчанию —
WHERE Default = TRUE; - 🌐 только сетевые —
WHERE Network = TRUE; - 🚫 убрать отключённые —
WHERE WorkOffline = FALSE; - 🏷️ только термопринтеры конкретной модели —
WHERE DriverName LIKE '%Zebra%'.
Последний фильтр полезен на складе. Виртуальные «принтеры» вроде Microsoft Print to PDF, OneNote и факса тоже попадают в Win32_Printer. Отсекайте их по драйверу или по порту: у PDF-принтера порт PORTPROMPT:, у реальных устройств — USB, IP или сетевой путь.
Тот же запрос можно отправить на другой компьютер, например на сервер печати. Для этого вместо точки в строке подключения укажите имя машины: winmgmts:\\PRINTSRV\root\cimv2. Удалённый WMI требует прав на целевом компьютере и открытого доступа в брандмауэре, поэтому для обычного пользователя такой вызов чаще всего завершится отказом в доступе. Для учёта парка принтеров это удобно, для повседневной печати не нужно.
Проверить запрос до того, как писать код, удобно в PowerShell: Get-CimInstance Win32_Printer | Select-Object Name, DriverName, PortName, Default. Если там список пустой, 1С тоже ничего не получит.
Список принтеров в управляемом приложении: где выполнять код
В управляемых формах главный вопрос — на какой стороне работает процедура. Принтеры, которые видит пользователь, установлены на его компьютере. Значит, код получения списка должен идти с директивой &НаКлиенте.
Если вызвать тот же код &НаСервере, вы получите принтеры сервера 1С. В клиент-серверном варианте служба сервера работает под отдельной учётной записью, и у неё принтеров пользователя нет. Отсюда классическая жалоба: «в файловой базе список был, на сервере пустой».
| Где запущен код | Что вернёт | Комментарий |
|---|---|---|
| Тонкий или толстый клиент, Windows | принтеры пользователя | основной сценарий |
| Сервер 1С на Windows | принтеры учётной записи службы | почти всегда не то, что нужно |
| Веб-клиент | ничего | COMОбъект недоступен, нужна директива #Если НЕ ВебКлиент |
| Клиент под Linux или macOS | ничего через COM | используйте команды ОС, см. следующий раздел |
| Терминальный сервер (RDP) | принтеры сеанса, включая перенаправленные | имена меняются между сеансами |
Вывод списка в форму и выбор принтера
Список удобно показать пользователю через СписокЗначений и немодальный выбор. Так код работает и при отключённой модальности:
&НаКлиенте
Процедура ВыбратьПринтер(Команда)
Список = Новый СписокЗначений;
Для Каждого Имя Из СписокПринтеровWSH() Цикл
Список.Добавить(Имя);
КонецЦикла;
Оповещение = Новый ОписаниеОповещения("ВыборПринтераЗавершение", ЭтотОбъект);
Список.ПоказатьВыборЭлемента(Оповещение, "Выберите принтер");
КонецПроцедуры
&НаКлиенте
Процедура ВыборПринтераЗавершение(ВыбранныйЭлемент, ДополнительныеПараметры) Экспорт
Если ВыбранныйЭлемент = Неопределено Тогда
Возврат;
КонецЕсли;
ИмяПринтера = ВыбранныйЭлемент.Значение; // реквизит формы
КонецПроцедуры
Терминальный сервер и перенаправленные принтеры
При работе через RDP Windows добавляет к имени локального принтера пользователя хвост вроде (перенаправлено 3). Номер меняется при каждом входе. Если сохранить полное имя, завтра печать уйдёт в никуда.
Решение — сохранять основу имени без хвоста, а при печати искать в текущем списке принтер, имя которого начинается с этой основы. Функция СтрНачинаетсяС() подходит для этого лучше всего.
Реестр Windows и командная строка как запасной путь
Бывает, что WScript.Network запрещён групповой политикой, а WMI на машине сломан. Тогда список берут из реестра или из вывода системных команд.
Где Windows хранит данные о принтерах:
- 👤
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Devices— все принтеры текущего пользователя, имя каждого принтера записано как имя параметра; - 🖥️
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Printers— локальные принтеры компьютера, по разделу на принтер; - ⭐
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows, параметрDevice— принтер по умолчанию в виде «Имя,winspool,порт».
Принтер по умолчанию читается одной строкой через WScript.Shell:
Оболочка = Новый COMОбъект("WScript.Shell");
Значение = Оболочка.RegRead("HKCU\Software\Microsoft\Windows NT\CurrentVersion\Windows\Device");
ПринтерПоУмолчанию = Лев(Значение, СтрНайти(Значение, ",") - 1);
Полный перечень удобнее получить командой и прочитать из файла. Пример через PowerShell, который вернёт по одному имени на строку:
&НаКлиенте
Функция СписокПринтеровPowerShell()
ИмяФайла = ПолучитьИмяВременногоФайла("txt");
Команда = "powershell -NoProfile -Command ""Get-Printer | ForEach-Object { $_.Name } | Out-File -Encoding UTF8 '"
+ ИмяФайла + "'""";
ЗапуститьПриложение(Команда, , Истина);
Текст = Новый ТекстовыйДокумент;
Текст.Прочитать(ИмяФайла, КодировкаТекста.UTF8);
Результат = Новый Массив;
Для НомерСтроки = 1 По Текст.КоличествоСтрок() Цикл
Имя = СокрЛП(Текст.ПолучитьСтроку(НомерСтроки));
Если ЗначениеЗаполнено(Имя) Тогда
Результат.Добавить(Имя);
КонецЕсли;
КонецЦикла;
УдалитьФайлы(ИмяФайла);
Возврат Результат;
КонецФункции
Командлет Get-Printer есть в Windows 8, Windows Server 2012 и новее. Вывод в файл с явной кодировкой UTF-8 спасает русские имена принтеров: без этого кириллица превращается в «кракозябры». Если используете reg query, учтите, что консоль Windows пишет в кодировке cp866.
На клиенте под Linux тот же приём работает с командой lpstat -a: она выводит принтеры CUPS, которые принимают задания. Принтер по умолчанию показывает lpstat -d.
Третий параметр ЗапуститьПриложение — ждать завершения программы. Без него 1С начнёт читать файл раньше, чем команда его запишет. В новых релизах платформы есть и асинхронный вариант этого метода.
Печать на выбранный принтер без диалога
Список принтеров почти всегда нужен ради одного: печатать сразу на нужное устройство. После выбора имя подставляют в табличный документ:
ТабДок.ИмяПринтера = ИмяПринтера;
ТабДок.КоличествоЭкземпляров = 1;
ТабДок.Напечатать(РежимИспользованияДиалогаПечати.НеИспользовать);
Имя должно совпадать с тем, что отдаёт Windows, символ в символ, включая пробелы и регистр. Поэтому не вводите его вручную в настройках, а берите из полученного списка.
Чтобы пользователю не выбирать принтер каждый раз, сохраните его выбор в серверной процедуре через ХранилищеОбщихНастроек.Сохранить(), а при открытии формы читайте через ХранилищеОбщихНастроек.Загрузить(). Перед печатью сверяйте сохранённое имя с текущим списком: принтер могли удалить или переименовать.
Ошибки при получении списка принтеров
Большинство сбоев видно по тексту сообщения. Разберите свой случай:
Enabled в разделе HKLM\Software\Microsoft\Windows Script Host\Settings: значение 0 означает запрет. Включать WSH или нет, решает администратор сети. Если нельзя, переходите на WMI или PowerShell.#Если НЕ ВебКлиент Тогда и для таких клиентов показывайте стандартный диалог печати.winmgmt /verifyrepository от имени администратора.&НаКлиенте.-Encoding UTF8 при записи, для reg query и cmd читайте файл в cp866.Отдельно о сетевых принтерах. Принтер, подключённый через \\Сервер\Принтер, попадает в список, даже если сервер печати сейчас выключен. Проверяйте свойство WorkOffline из WMI до отправки задания, иначе пользователь узнает о проблеме только когда пойдёт за пустым лотком.
Частые вопросы
Можно ли получить список принтеров в веб-клиенте 1С?
Нужны ли права администратора, чтобы прочитать список?
WScript.Network, и запрос к Win32_Printer работают с правами обычного пользователя. Администратор нужен только для установки драйверов и проверки репозитория WMI.Почему в списке есть «Microsoft Print to PDF» и «Fax»?
Как узнать, какой принтер стоит по умолчанию?
WHERE Default = TRUE в запросе к Win32_Printer. Без WMI — прочитать параметр Device в разделе реестра текущего пользователя.