Скрипт для рестарта задания печати.

Скрипт был написан по итогам обсуждения ветки Печать выбранных документов заново. Задача была сформулирована следующим образом:

Есть сервер с win2008 r2 sp1. На нем развернут терминал 1с, установлены принтеры, порядка 30. К серверу подключаются удаленные объекты, соединенные VPN каналом. Скорость канала варьируется от 96-512 Кбит/с. Периодически, при печати на принтер, задание останавливается ошибкой и висит со статусом "ошибка". Для продолжения работы необходимо открыть принтер, выбрать задание с ошибкой правой кнопкой, и нажать "Перезапустить". Задание перезапускается и печатается.

Так как принтеров большое кол-во и задания зависают ошибкой довольно часто, есть возможность проверять принтеры на ошибку скриптом. В инете нашел такой скрипт:
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
    & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colPrintJobs =  objWMIService.ExecQuery _
    ("Select * from Win32_PrintJob where status=’Error’")
For Each objPrintJob in colPrintJobs
    objPrintJob.Resume
Next
Однако, этот скрипт возобновляет печать, только если задание было приостановлено, а перезапускать не хочет. Перезапуск спулера помогает, однако при этом перезапускаются все параллельные очереди на других принтерах. Есть ли метод или скрипт для класса objPrintJob, чтобы перезапустить задание, как это можно сделать визуально?

Оказывается, средствами WMI невозможно выполнить такую простую вещь, как рестарт задания печати. Win32_PrintJob позволяет только  приостановить и продолжить приостановленное задание. Поэтому пришлось использовать .Net и набить себе шишку, при его использовании. И так, в ходе обсуждения выяснилось, что задание печати можно перезапустить, используя объект PrintSystemJobInfo. Из описания на msdn узнаем, что коллекцию этих объектов можно получить при помощи метода GetPrintJobInfoCollection объекта PrintQueue. Ну, а перечень объектов PrintQueue, в свою очередь, можно получить при помощи метода GetPrintQueues объекта PrintServer. Казалось бы все просто:

#Подключаем сборку System.Printing
Add-Type -AssemblyName System.Printing
#Создаем объект PrintServer (для локального компьютера)
$PrintServer = new-object System.Printing.PrintServer
#Получаем перечень очередей печати (PrintQueues)
$PrintQueues = $PrintServer.GetPrintQueues()

Но последняя строчка не хочет выполняться, вместо результата я получаю сообщение об ошибке:

PS(18)> $PrintQueues = $PrintServer.GetPrintQueues()

Исключение при вызове "GetPrintQueues" с "0" аргументами: "Вызывающий поток не может получить доступ к данному объекту,

так как владельцем этого объекта является другой поток."

строка:1 знак:43

+ $PrintQueues = $PrintServer.GetPrintQueues <<<< ()

    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException

    + FullyQualifiedErrorId : DotNetMethodException

“Гугление” по этой ошибке никакой новой информации не дало. Из сообщения об ошибке и так было понятно, что один поток не может получить доступ к объекту другого потока. Наиболее часто встречающаяся рекомендация для устранения этой ошибки заключалась в том, чтобы использовать метод Invoke объекта Dispatcher для выполнения “проблемного” действия, но, честно говоря, я так не додумался, как его можно использовать в данном конкретном случае. Но, оказалось, что выход, все-таки, есть: начиная с PowerShell версии 2.0, мы можем задать способ использования потоковой модели, присвоив нужное значение объекту Host.Runspace.ThreadOptions. Что, собственно, и было немедленно проделано:

#Определяем способ работы с потоками (threads)
$host.Runspace.ThreadOptions = "ReuseThread"

 

Default On the local computer, UseNewThread is used for runspaces and ReuseThread is used for runspace pools. Server settings are used for remote runspaces and runspace pools. This field is introduced in Windows PowerShell 2.0.
ReuseThread Creates a new thread for the first invocation and then re-uses that thread in subsequent invocations. This field is introduced in Windows PowerShell 2.0.
UseCurrentThread Execution occurs on the thread that called the Invoke method. This option is not valid for asynchronous calls. This field is introduced in Windows PowerShell 2.0.
UseNewThread Creates a new thread for each invocation. This field is introduced in Windows PowerShell 2.0.

 

И так, проблему с потоками мы устранили, ну, а все остальное – очевидно, потому просто опубликую конечный результат:

#Определяем способ работы с потоками (threads)
$host.Runspace.ThreadOptions = "ReuseThread"
#Подключаем сборку System.Printing
Add-Type -AssemblyName System.Printing
#Создаем объект PrintServer (для локального компьютера)
$PrintServer = new-object System.Printing.PrintServer
#Получаем перечень очередей печати (PrintQueues)
$PrintQueues = $PrintServer.GetPrintQueues()
#Для каждой очереди печати...
foreach ($PrnQueue in $PrintQueues) {
    #...обновим свойства объекта PrnQueue значениями, полученными от принтера 
    #и утилит очередей печати, запущенных на компьютере
    $PrnQueue.Refresh()
    #Получаем перечень заданий печати для текущей Очереди печати
    $PrintJobs=$PrnQueue.GetPrintJobInfoCollection()
    #Для каждого задания печати...
    foreach ($PrnJob in $PrintJobs) {
        #...проверяем статус задания
        if ($PrnJob.JobStatus.ToString() -like "*error*") {
            #если статус задания, содержит слово error, то перезапускаем задание.
            $PrnJob.Restart()
        }
    }
}

Upd. Вот, нашел еще одну интересную статью по управлению принтерами из PowerShell: Printer Management Using PowerShell

3 Comments

  1. Спасибо огромное – Ваш скрипт прекрасно работает.
    Можете помочь пожалуйста, я не силен в программировании – хотелось бы добавить в скрипт строчку –
    что бы после того, как перезапустится задание с ошибкой – в отдельный текстовый документ (лог) записывалось дата, время, и имя принтера где была ошибка.
    (впрочем, и название документа не помешало бы)

    Буду премного благодарен!!!!!!!!!!!!!

Leave a Reply

Your email address will not be published. Required fields are marked *

Notify me of followup comments via e-mail. You can also subscribe without commenting.