При работе в 1С через удаленный рабочий стол (RDP) часто возникает проблема с автоматическим выбором принтера. Если мы пытаемся задать имя принтера для табличного документа, используя конструкцию ТабДок.ИмяПринтера = "Имя_Принтера", то сталкиваемся с тем, что при перенаправлении через RDP к имени принтера добавляется суффикс "(перенаправлено N)", где N — это уникальный идентификатор сеанса пользователя. Из-за этого изменяющегося суффикса стандартный способ задания имени принтера не всегда работает корректно. Например, если задать ТабДок.ИмяПринтера = "BIXOLON XT5-40 (перенаправлено 97)", печать будет осуществляться правильно, но при изменении номера сеанса (например, на 98) принтер уже не будет найден. В то же время, если задать просто ТабДок.ИмяПринтера = "BIXOLON XT5-40", то принтер не будет проставлен автоматически, так как полное имя отличается. В этой статье мы разберем различные подходы к решению данной проблемы, включая получение списка принтеров и поиск нужного по части имени, а также более надежные методы с использованием системных средств Windows.
Для начала, давайте выясним причину такого поведения. Когда принтер перенаправляется через RDP, операционная система добавляет к его имени специальные суффиксы, чтобы обеспечить уникальность каждого перенаправленного принтера в рамках различных пользовательских сеансов. Это стандартное поведение Windows, направленное на предотвращение конфликтов. Например, принтер "BIXOLON XT5-40" может быть виден как "BIXOLON XT5-40 (перенаправлено 97)" или "BIXOLON XT5-40 (перенаправлено 98)". Этот суффикс делает затруднительной надежную идентификацию принтера по его "короткому" имени.
Для решения этой проблемы нам необходимо получить список всех доступных принтеров в текущем сеансе и найти нужный нам принтер по известной нам части его имени. Затем мы можем использовать полное имя найденного принтера для свойства ТабДок.ИмяПринтера.
Один из самых простых способов получить список принтеров в 1С — это использование COM-объекта WScript.Network. Давайте посмотрим, как это можно реализовать.
Сначала мы создадим объект WScript.Network, который предоставляет доступ к функциям управления сетью, включая информацию о принтерах. Затем мы используем метод EnumPrinterConnections(), который возвращает коллекцию подключенных принтеров. Эта коллекция содержит пары значений: имя порта и имя принтера.
Рассмотрим пример кода:
СписокПринтеров = Новый СписокЗначений;
Попытка
// Создаем COM-объект WScript.Network для работы с сетевыми подключениями
Принтеры = Новый COMОбъект("WScript.Network");
// Получаем коллекцию подключенных принтеров
Prn = Принтеры.EnumPrinterConnections();
// Перебираем коллекцию, чтобы получить имена принтеров
// Коллекция Prn содержит пары: Prn.Item(i) - имя порта, Prn.Item(i+1) - имя принтера
Для i = 0 По Prn.Count() - 1 Шаг 2 Цикл
// Добавляем имя принтера в список
СписокПринтеров.Добавить(Prn.Item(i+1));
КонецЦикла;
Исключение
Сообщить("Ошибка при получении списка принтеров через WScript.Network: " + ОписаниеОшибки());
КонецПопытки;
Если СписокПринтеров.Количество() > 0 Тогда
Сообщить("Список доступных принтеров:");
Для Каждого Принтер Из СписокПринтеров Цикл
Сообщить(Принтер.Значение);
КонецЦикла;
Иначе
Сообщить("Не удалось получить список принтеров.");
КонецЕсли;
// Теперь найдем наш принтер по части имени
ИскомоеИмяПринтера = "BIXOLON XT5-40";
НайденныйПринтер = "";
Для Каждого ЭлементСписка Из СписокПринтеров Цикл
Если СтрНайти(Врег(ЭлементСписка.Значение), Врег(ИскомоеИмяПринтера)) > 0 Тогда
НайденныйПринтер = ЭлементСписка.Значение;
Прервать;
КонецЕсли;
КонецЦикла;
Если Не ПустаяСтрока(НайденныйПринтер) Тогда
Сообщить("Принтер '" + ИскомоеИмяПринтера + "' найден: " + НайденныйПринтер);
// Теперь мы можем использовать это полное имя для ТабДок.ИмяПринтера
// ТабДок.ИмяПринтера = НайденныйПринтер;
Иначе
Сообщить("Принтер '" + ИскомоеИмяПринтера + "' не найден среди доступных.");
КонецЕсли;
Важный момент: Как показала практика (сообщение 5), объект WScript.Network может не всегда видеть принтеры, проброшенные через RDP. Он чаще всего отображает только локальные принтеры (если код выполняется на клиентской машине или на сервере, но не в RDP сессии) или сетевые принтеры, подключенные напрямую. Если ваш принтер проброшен через RDP, особенно с использованием технологии Remote Desktop Easy Print, он может не появиться в списке, полученном этим методом.
Для более надежного и полного получения списка принтеров, включая те, что перенаправлены через RDP, мы можем использовать WMI (Windows Management Instrumentation). WMI предоставляет мощный интерфейс для управления операционной системой и ее компонентами. Мы можем получить список принтеров с помощью класса Win32_Printer. Этот метод более универсален и часто позволяет видеть все принтеры, доступные в текущем сеансе, включая RDP-перенаправленные.
Разберем шаги получения списка принтеров через WMI:
winmgmts:\\.\root\CIMV2. Это основной объект WMI, который позволяет выполнять запросы к системе.SELECT * FROM Win32_Printer. Этот запрос вернет коллекцию всех принтеров, зарегистрированных в системе.Посмотрим на пример кода 1С:
СписокПринтеровWMI = Новый СписокЗначений;
Попытка
// Создаем COM-объект для доступа к WMI
WMI = Новый COMОбъект("winmgmts:\\.\root\CIMV2");
// Выполняем WQL-запрос для получения всех принтеров
// Класс Win32_Printer содержит информацию о принтерах
ПринтерыКоллекция = WMI.ExecQuery("SELECT * FROM Win32_Printer");
// Перебираем найденные принтеры
Для Каждого ПринтерWMI Из ПринтерыКоллекция Цикл
// ПринтерWMI.Name содержит полное имя принтера
// ПринтерWMI.PortName содержит имя порта
// ПринтерWMI.DriverName содержит имя драйвера
// ПринтерWMI.Local указывает, является ли принтер локальным
// ПринтерWMI.Network указывает, является ли принтер сетевым
// ПринтерWMI.Shared указывает, является ли принтер общим
СписокПринтеровWMI.Добавить(ПринтерWMI.Name);
КонецЦикла;
Исключение
Сообщить("Ошибка при получении списка принтеров через WMI: " + ОписаниеОшибки());
КонецПопытки;
Если СписокПринтеровWMI.Количество() > 0 Тогда
Сообщить("Список доступных принтеров через WMI:");
Для Каждого Принтер Из СписокПринтеровWMI Цикл
Сообщить(Принтер.Значение);
КонецЦикла;
Иначе
Сообщить("Не удалось получить список принтеров через WMI.");
КонецЕсли;
// Теперь найдем наш принтер по части имени из списка WMI
ИскомоеИмяПринтера = "BIXOLON XT5-40";
НайденныйПринтерWMI = "";
Для Каждого ЭлементСписка Из СписокПринтеровWMI Цикл
Если СтрНайти(Врег(ЭлементСписка.Значение), Врег(ИскомоеИмяПринтера)) > 0 Тогда
НайденныйПринтерWMI = ЭлементСписка.Значение;
Прервать;
КонецЕсли;
КонецЦикла;
Если Не ПустаяСтрока(НайденныйПринтерWMI) Тогда
Сообщить("Принтер '" + ИскомоеИмяПринтера + "' найден через WMI: " + НайденныйПринтерWMI);
// Используем найденное полное имя для ТабДок.ИмяПринтера
// ТабДок.ИмяПринтера = НайденныйПринтерWMI;
Иначе
Сообщить("Принтер '" + ИскомоеИмяПринтера + "' не найден среди доступных через WMI.");
КонецЕсли;
Этот метод считается более предпочтительным для работы с принтерами в Windows, так как предоставляет более полную и надежную информацию.
В современных версиях Windows Server для печати из RDP-сессии активно используется универсальный драйвер Remote Desktop Easy Print. Этот драйвер позволяет печатать практически на любом принтере, подключенном к клиентской машине, без необходимости установки специфических драйверов на RDP-сервере. Это значительно упрощает администрирование. Принтеры, проброшенные через Easy Print, также будут иметь суффикс "(redirected N)", и именно их мы чаще всего ищем в RDP-сессии.
При использовании WMI, эти принтеры также будут присутствовать в коллекции Win32_Printer, и их можно будет идентифицировать по имени, которое содержит как исходное имя принтера, так и метку перенаправления. Мы также можем дополнительно проверять свойство DriverName, которое для принтеров Easy Print будет, как правило, содержать "Remote Desktop Easy Print" или подобное значение.
Помимо программного поиска, мы можем рассмотреть несколько организационных и технических подходов для минимизации проблем с печатью через RDP.
Get-Printer | ? DriverName -eq "Remote Desktop Easy Print") или функции WinAPI (EnumPrinters) для получения подробной информации о принтерах. Однако их интеграция с 1С потребует либо выполнения внешних скриптов, либо использования объектов COM-объектов для вызова функций WinAPI, что является более сложной задачей для программиста 1С.Программный выбор принтера в 1С при работе через RDP требует внимательного подхода из-за динамического изменения имен принтеров. Мы рассмотрели несколько методов для получения списка доступных принтеров. Метод с использованием WScript.Network прост в реализации, но может не видеть все перенаправленные RDP-принтеры. Метод с использованием WMI (Windows Management Instrumentation) через класс Win32_Printer является более универсальным и надежным, предоставляя доступ к полному списку принтеров, включая те, что используют Remote Desktop Easy Print.
Рекомендуем использовать WMI для получения списка принтеров, а затем производить поиск по части имени. Это позволит нам найти нужный принтер, даже если его полное имя изменяется из-за суффикса "(перенаправлено N)". Помните, что комплексное решение проблемы может включать как программные доработки в 1С, так и правильную настройку инфраструктуры RDP и принтеров. При возникновении проблем всегда полезно проверять журналы событий Windows на сервере и клиенте для выявления ошибок, связанных с драйверами или службой печати.