[ Team LiB ] Previous Section Next Section

22.4 Manipulating Print Queues and Print Jobs

So far we've shown you how to use ADSI to manipulate persistent and dynamic objects, such as shares, sessions, and resources. Now we're going to examine printer queues and jobs. In this section, we're going to lead you through creating scripts to do the following:

  • Identify print queues in Active Directory

  • Bind to a print queue[4] and access its properties

    [4] Print queues are logical ADSI names for printers installed on a computer.

  • List the print jobs in a print queue and manipulate them

All the code in these scripts for managing printers is done using the WinNT provider, so it will work on Windows NT as well as Active Directory. The LDAP searches will not work on Windows NT.

One point before we go on: at the end of Chapter 20, we detail a function called SearchAD. We need to use it now to search Active Directory for the printer's ADsPath and store it in arrSearchResults(0,0).

22.4.1 Identifying Print Queues in Active Directory

List-Print-Queue.vbs in Example 22-2 is a heavily commented script, so it should be easy to follow.

Example 22-2. List-Print-Queue.vbs identifies print queues in Active Directory
Option Explicit
On Error Resume Next
   
'**********************************************************************
'Active Directory path to start the search from
'**********************************************************************
Const strDomainToSearch = "LDAP://dc=mycorp,dc=com"
   
'**********************************************************************
'Maximizes the Notepad screen when started
'**********************************************************************
Const vbMaximizedFocus   = 3
   
'**********************************************************************
'Sets the location of the temporary file
'**********************************************************************
Const TEMPFILE = "C:\PRINTERLIST-TEMP.TXT"
   
'**********************************************************************
'Opens a file and lets you start writing from the beginning of the file
'**********************************************************************
Const ForWriting = 2
   
Dim arrPaths(  ), fso, ts, strItem, intRC, objShell, intIndex
   
If Not SearchAD(strDomainToSearch,"(objectClass=printQueue)","SubTree",arrPaths) Then
  MsgBox "Printer listing failed!"
Else
  '**********************************************************************
  'Opens the temporary text file for writing. If the text file already 
  'exists, overwrite it.
  '**********************************************************************
  Set fso = CreateObject("Scripting.FileSystemObject")
  Set ts = fso.OpenTextFile(TEMPFILE, ForWriting, True)
   
  '**********************************************************************
  ' Writes out the printer ADsPaths
  '**********************************************************************
  ts.WriteLine "Total printers in Active Directory: " & UBound(arrPaths)+1
  ts.WriteLine
  For intIndex=0 To UBound(arrPaths)
    ts.WriteLine arrPaths(intIndex,1)
    ts.WriteLine vbTab & arrPaths(intIndex,0)
  Next
  ts.Close
   
  '**********************************************************************
  'Sets the third parameter of the Shell::Run method to TRUE, which
  'allows the script to open up the file in Notepad and maximize the
  'screen. The script stops executing until you close Notepad, which 
  'places a return code into intRC. When Notepad is closed, the script
  'deletes the file.
  '**********************************************************************
  Set objShell = CreateObject("WScript.Shell")
  intRC = objShell.Run ("notepad.exe " & TEMPFILE, vbMaximizedFocus, TRUE)
  fso.DeleteFile(TEMPFILE)
End If

The script uses the search function to search Active Directory for all objects of class printQueue, writes their ADsPath and cn attributes out to a temporary file, displays the file for you in Notepad, and then erases the file when Notepad is closed. The code for opening and closing files and displaying them with Notepad is the same as that in the ShowUsers.vbs script earlier in the chapter. Here is an example of the output from this program:

Total printers in Active Directory: 3
   
DC1-ph_stores_hp4000
  LDAP://CN=DC1-stores_hp4000,CN=DC1,OU=Domain Controllers,DC=mycorp,DC=com
SS-0001-Alex&Mark
  LDAP://CN=COMPUTER-0789-Alex&Mark,CN=COMPUTER-0789,OU=Finances, _
    OU=Finance Clients,OU=Clients,DC=mycorp,DC=com
ZZ-NT0089-HP LaserJet 4M Plus
  LDAP://CN=ZZ-0089-HP LaserJet 4M Plus,CN=ZZ-0089,OU=Finances, _
    OU=Finance Clients,OU=Clients,DC=mycorp,DC=com

The lines are too long to fit on the page, so we have broken them up with underscores as we would do if this were a script. They normally would be unbroken.

Let's take a look at the output for a moment. The first PrintQueue object is called "DC1-stores_hp4000" and is held within the DC1 domain controller, as if that DC were itself a container object. Computer objects are a special case and can act as containers and hold other objects beneath them. The computer called DC1 (actually an Active Directory domain controller) is actually the parent of this printQueue object. We could go through listing the properties of the PrintQueue objects for you in a script, but this is very easy to do, and to save this chapter from getting any longer, you can find the print queue properties yourself on MSDN.

22.4.2 Binding to a Print Queue

Unfortunately, we cannot connect to this printQueue object and list the jobs because the Active Directory object that we have connected to is only the advertisement or publication that such a queue exists. To connect to the printer object that holds the jobs and that we can manipulate, we need to use the WinNT namespace.

While we could provide a simple piece of code to connect to a queue, we'd like to modify the previous script to show you how that could be accomplished. We'll list the queues as before, but this time, we'll also bind to the first queue that we find (and only the first) and print out some properties.

To see what we need to do, let's take a look at the first queue in the previous output. The actual printer path that we need to connect to is:

WinNT://MYCORP/DC1/stores_hp4000

We need to massage the data returned by the SearchAD function to produce the information about the computer name and the printer name. List-Print-Queue-2.vbs is the result, a modified version of List-Print-Queue.vbs with two extra sets of information provided. The first is a new constant to define the workgroup or domain; the second we'll go through after the script in Example 22-3.

Example 22-3. List-Print-Queue-2.vbs binds to the print queue
Option Explicit
On Error Resume Next
   
'**********************************************************************
' Sets the domain or workgroup that the servers or workstations reside in
'**********************************************************************
Const strDomainOrWorkGroup = "MYCORP"
   
'**********************************************************************
'Active Directory path to start the search from
'**********************************************************************
Const strDomainToSearch = "LDAP://dc=mycorp,dc=com"
   
'**********************************************************************
'Maximizes the Notepad screen when started
'**********************************************************************
Const vbMaximizedFocus   = 3
   
'**********************************************************************
'Sets the location of the temporary file
'**********************************************************************
Const TEMPFILE = "C:\PRINTERLIST-TEMP.TXT"
   
'**********************************************************************
'Opens a file and lets you start writing from the beginning of the file
'**********************************************************************
Const ForWriting = 2
   
Dim arrPaths(  ), fso, ts, strItem, intRC, objShell, intIndex, strComputer
Dim strPrinter, objPrinter
   
If Not _
  SearchAD(strDomainToSearch,"(objectClass=printQueue)","SubTree",arrPaths) Then
    MsgBox "Printer listing failed!"
Else
  '**********************************************************************
  'Opens the temporary text file for writing. If the text file already 
  'exists, overwrite it
  '**********************************************************************
  Set fso = CreateObject("Scripting.FileSystemObject")
  Set ts = fso.OpenTextFile(TEMPFILE, ForWriting, True)
   
  '**********************************************************************
  ' Writes out the printer ADsPaths
  '**********************************************************************
  ts.WriteLine "Total printers in Active Directory: " & UBound(arrPaths)+1
  ts.WriteLine
  For intIndex=0 To UBound(arrPaths)
    ts.WriteLine arrPaths(intIndex,1)
    ts.WriteLine vbTab & arrPaths(intIndex,0)
  Next
  ts.WriteLine
   
  '**********************************************************************
  'Bind to the first printer and list the properties
  '**********************************************************************
  strComputer = Split(arrPaths(0,0),",")(1)
  strComputer = Right(strComputer,Len(strComputer) - 3)
  strPrinter = Right(arrPaths(0,1),Len(arrPaths(0,1)) - Len(strComputer) - 1)
  Set objPrinter = GetObject("WinNT://" & strDomainOrWorkGroup & "/" _
    & strComputer & "/" & strPrinter)
  ts.WriteLine "Name        : " & objPrinter.Name
  ts.WriteLine "Status      : " & objPrinter.Status
  ts.WriteLine "Model       : " & objPrinter.Model
  ts.WriteLine "Location    : " & objPrinter.Location
  ts.WriteLine "PrinterPath : " & objPrinter.PrinterPath
  ts.Close
   
  '**********************************************************************
  'Sets the third parameter of the Shell::Run method to TRUE, which
  'allows the script to open up the file in Notepad and maximize the
  'screen. The script stops executing until you close Notepad, which 
  'places a return code into intRC. When Notepad is closed, the script
  'deletes the file.
  '**********************************************************************
  Set objShell = CreateObject("WScript.Shell")
  intRC = objShell.Run ("notepad.exe " & TEMPFILE, vbMaximizedFocus, TRUE)
  fso.DeleteFile(TEMPFILE)
End If

To bind to the printer and list the properties, the script first splits the entire LDAP path of the first array element, using the comma as the delimiter. It then immediately retrieves the second item (indexed as 1 because item numbers start at 0) and adds it to strComputer. You use VBScript's Split function like this:

arrResults = Split("Moose 1,Moose 2,Penguin,Banana,Squirrel,Hamster",",")

This results in arrResults(0) containing Moose 1 and arrResults(5) containing Hamster. Instead of passing the results out to an array, we can directly retrieve one value from that array by passing the index value to the Split function. To retrieve and print Squirrel from the preceding string, we use the following code:

MsgBox Split("Moose 1,Moose 2,Penguin,Banana,Squirrel,Hamster",",")(4)

You can see that here we don't need arrResults at all. That's how Split works in the previous code.

The first item returned is "CN=DC1". We can then use the VBScript Right function to take the righthand part of that string—ignoring the first three characters, i.e., DC1—and put the result back into the strComputer variable. We now need the printer name. This is done by taking the right-hand part of the cn attribute returned (DC1- stores_hp4000) and ignoring the number of characters equal to the length of the computer name. That yields "stores_hp4000". We then can assemble all the pieces and bind to the printer object on that computer. We finally print out five attributes (IADs::Name, IADsPrintQueueOperations::Status, IADsPrintQueue::Model, IADsPrintQueue::Location, and IADsPrintQueue::PrinterPath) of that printer to confirm that the printer exists.

22.4.3 IADsPrintQueueOperations and Print Queues

Having successfully connected to a print queue, you can then use the IADsPrintQueueOperations interface to its full extent. This interface has methods with names like Pause, Resume, and Purge that you should recognize; they correspond to specific print queue functions. There is one important property status that is also available and allows you to query the status of the printer. While List-Print-Queue-2.vbs just prints out this value as an integer, Display-Print-Queue-Status.vbs is a script that binds to the same printer and uses a Select Case statement to print the status out using the MsgBox function. This script is listed in Example 22-4.

Example 22-4. Display-Print-Queue-Status.vbs uses MsgBox to display printer status
'**********************************************************************
'IADsPrintQueueOperations::Status values
'**********************************************************************
Const ADS_PRINTER_PAUSED = &H00000001
Const ADS_PRINTER_PENDING_DELETION = &H00000002
Const ADS_PRINTER_ERROR = &H00000003
Const ADS_PRINTER_PAPER_JAM = &H00000004
Const ADS_PRINTER_PAPER_OUT = &H00000005
Const ADS_PRINTER_MANUAL_FEED = &H00000006
Const ADS_PRINTER_PAPER_PROBLEM = &H00000007
Const ADS_PRINTER_OFFLINE = &H00000008
Const ADS_PRINTER_IO_ACTIVE = &H00000100
Const ADS_PRINTER_BUSY = &H00000200
Const ADS_PRINTER_PRINTING = &H00000400
Const ADS_PRINTER_OUTPUT_BIN_FULL = &H00000800
Const ADS_PRINTER_NOT_AVAILABLE = &H00001000
Const ADS_PRINTER_WAITING = &H00002000
Const ADS_PRINTER_PROCESSING = &H000040000
Const ADS_PRINTER_INITIALIZING = &H00008000
Const ADS_PRINTER_WARMING_UP = &H00010000
Const ADS_PRINTER_TONER_LOW = &H00020000
Const ADS_PRINTER_NO_TONER = &H00040000
Const ADS_PRINTER_PAGE_PUNT = &H00080000
Const ADS_PRINTER_USER_INTERVENTION = &H00100000
Const ADS_PRINTER_OUT_OF_MEMORY = &H00200000
Const ADS_PRINTER_DOOR_OPEN = &H00400000
Const ADS_PRINTER_SERVER_UNKNOWN = &H00800000
Const ADS_PRINTER_POWER_SAVE = &H01000000
   
'**********************************************************************
' Bind to the printer
'**********************************************************************
Set objPrinter = GetObject("WinNT://MYCORP/DC1/stores_hp4000")
   
'**********************************************************************
' Print out the queue status
'**********************************************************************
Select Case objPrinter.Status
  Case 0
    MsgBox "On line"
  Case ADS_PRINTER_PAUSED
    MsgBox "Paused"
  Case ADS_PRINTER_PENDING_DELETION
    MsgBox "Pending deletion"
  Case ADS_PRINTER_ERROR
    MsgBox "Printer error"
  Case ADS_PRINTER_PAPER_JAM
    MsgBox "Paper jam"
  Case ADS_PRINTER_PAPER_OUT
    MsgBox "Out of paper"
  Case ADS_PRINTER_MANUAL_FEED
    MsgBox "Manual feed pending"
  Case ADS_PRINTER_PAPER_PROBLEM
    MsgBox "Paper trouble"
  Case ADS_PRINTER_OFFLINE
    MsgBox "Offline"
  Case ADS_PRINTER_IO_ACTIVE
    MsgBox "We/O active"
  Case ADS_PRINTER_BUSY
    MsgBox "Printer busy"
  Case ADS_PRINTER_PRINTING
    MsgBox "Printing"
  Case ADS_PRINTER_OUTPUT_BIN_FULL
    MsgBox "Output bin full"
  Case ADS_PRINTER_NOT_AVAILABLE
    MsgBox "Not available"
  Case ADS_PRINTER_WAITING
    MsgBox "Waiting"
  Case ADS_PRINTER_PROCESSING
    MsgBox "Processing"
  Case ADS_PRINTER_INITIALIZING
    MsgBox "Initializating"
  Case ADS_PRINTER_WARMING_UP
    MsgBox "Warming up"
  Case ADS_PRINTER_TONER_LOW
    MsgBox "Toner low"
  Case ADS_PRINTER_NO_TONER
    MsgBox "Without toner"
  Case ADS_PRINTER_PAGE_PUNT
    MsgBox "Page punt"
  Case ADS_PRINTER_USER_INTERVENTION
    MsgBox "User intervention required"
  Case ADS_PRINTER_OUT_OF_MEMORY
    MsgBox "Out of memory"
  Case ADS_PRINTER_DOOR_OPEN
    MsgBox "Door open"
  Case ADS_PRINTER_SERVER_UNKNOWN
    MsgBox "Server unknown"
  Case ADS_PRINTER_POWER_SAVE
    MsgBox "Power save"
  Case Else
    MsgBox "UNKNOWN"
End Select

The final important IADsPrintQueueOperations method that is available to you is IADsPrintQueueOperations::PrintJobs, which returns a collection of print jobs that you can interact with using IADsCollection.

22.4.4 Print Jobs

The IADsPrintQueueOperations::PrintJobs method allows you to obtain a collection object that you then can use in a For Each...Next loop. You can pause and resume the jobs using methods of the same name from the IADsPrintJobOperations interface. In addition, as the collection represents the underlying print jobs, you also can use the IADsCollection::Add and IADsCollection::Remove methods to add and remove print jobs from the collection. The Add method is not of much use here, but the Remove method is, since this allows you to delete jobs from the queue. Assuming we had bound successfully to the queue as before, this section of code would purge the queue manually. The following code gives you some idea of what you can do:

For Each objJob in objPrinter.PrintJobs
  objPrinter.PrintJobs.Remove (objJob.Name)
Next

Example 22-5 demonstrates that each job has a number of attributes from IADsPrintJob and IADsPrintJobOperations you can print. This is not the definitive list, and we urge you to check out MSDN for the full set.

Example 22-5. Display some properties and the status of each print job
'**********************************************************************
'IADsPrintJobOperations::Status values
'**********************************************************************
Const ADS_JOB_PAUSED = &H00000001 
Const ADS_JOB_ERROR = &H00000002 
Const ADS_JOB_DELETING = &H00000004 
Const ADS_JOB_PRINTING = &H00000010 
Const ADS_JOB_OFFLINE = &H00000020 
Const ADS_JOB_PAPEROUT = &H00000040 
Const ADS_JOB_PRINTED = &H00000080 
Const ADS_JOB_DELETED = &H00000100 
   
'**********************************************************************
' Bind to the printer
'**********************************************************************
Set objPrinter = GetObject("WinNT://MYCORP/DC1/stores_hp4000")
   
'**********************************************************************
'Print out some properties and the status of each job
'**********************************************************************
For Each objJob in objPrinter.PrintJobs
  str = "Name: " & objJob.Name & vbCrLf
  str = str & "Position: " & objJob.Position & vbCrLf
  str = str & "Size: " & objJob.Size & vbCrLf
  str = str & "Total Pages: " & objJob.TotalPages & vbCrLf
  str = str & "Pages Printed: " & objJob.PagesPrinted & vbCrLf
  Select Case objJob.Status
   
    Case 0
      str = str & "Status : " & "OK"
    Case ADS_JOB_PAUSED
      str = str & "Status : " & "Paused"
    Case ADS_JOB_ERROR
      str = str & "Status : " & "Error"
    Case ADS_JOB_DELETING
      str = str & "Status : " & "Deleting"
    Case ADS_JOB_PRINTING
      str = str & "Status : " & "Printing"
    Case ADS_JOB_OFFLINE
      str = str & "Status : " & "Offline"
    Case ADS_JOB_PAPEROUT
      str = str & "Status : " & "Paper Out"
    Case ADS_JOB_PRINTED
      str = str & "Status : " & "Printed"
    Case ADS_JOB_DELETED
      str = str & "Status : " & "Deleted"
    Case Else
      str = str & "Status : " & "Unknown"
  End Select
   
  MsgBox str
Next

Again, just as in Display-Print-Queue-Status.vbs, the IADsPrintJobOperations::Status property method has a defined set of constants that can be used to tell you about a job. One thing to note is that IADsPrintJobOperations::Position is a read/write value, so you can use this to move jobs around in the queue print sequence. Actually, a number of IADsPrintJob property methods are also read/write: IADsPrintJob::StartTime and IADsPrintJob::UntilTime (to set a future time before which the job can be printed), IADsPrintJob::Priority, IADsPrintJob::Description, and IADsPrintJob::Notify, plus IADsPrintJob::NotifyPath (the user is contacted when the job is printed).

    [ Team LiB ] Previous Section Next Section