Downloads
101483.zip

Executive Summary:
Troubleshooting printer problems in a large environment where multiple techs can make configuration changes can be difficult unless you're recording all changes over time. PrinterInfo.vbs can be run as often as needed to capture print queue data with a simple Windows Management Instrumentation (WMI) VBScript that writes data to an Excel spreadsheet and an XML-based database. PrinterInfoCompare.hta, an HTML Application (HTA), provides a simple and easy-to-use GUI for performing comparisons on the data collected by PrinterInfo.

I've faced a couple of ordeals troubleshooting printer problems where I didn’t have a reference to past printer configurations, so I decided to start keeping a history of all my print queues. I’ve found that having this history is particularly helpful in my environment because several other people have rights to maintain and troubleshoot printer problems, namely senior Help desk techs and senior desktop techs. They're the level 1 and level 2 technicians who get first crack at resolving printer problems. However, if they can’t resolve a problem, I'm the one who ultimately ends up with the support ticket. So I devised a solution for capturing my print queue data with a simple script; I can then compare the data from the current state to past states to reveal any changes and often discover problems more quickly.

A Little Background on the Problem
In addition to having eleven print servers and many printers, the number of queues in my environment is exceptionally high—more than 1000—because many of the printers have multiple queues set up, some with PostScript drivers, some with Printer CL (PCL) drivers, and some with drivers that might appear to be incorrect because they don’t match the make or model of the printer.

Having multiple techs with different levels of expertise in such an environment has its advantages. But it has its disadvantages as well. I've seen cases where someone has changed settings on a print queue while trying to troubleshoot a problem, then failed to return the original settings after discovering that the modification didn't resolve the problem. I've also frequently found that someone changed a driver from PCL to PostScript or PostScript to PCL to get a user's document to print. When the change lets the user successfully print, the tech assumes that all is well, but in fact a new problem was created for users who need the original driver.

I’ve seen printers that needed to have a competitor’s driver installed so they would function to a certain specification that the original manufacturer's driver couldn't meet. At first sight, I can see why someone would say, “Hey, that can’t be right. No wonder the user can’t print.” So the tech changes the driver to one that matches the printer; however, now documents don’t print for the users who needed the specialized driver, and unfortunately the technician doesn’t remember what the original driver was and can’t reset it.

Another problem I occasionally run up against is that a print queue has been renamed, something that generally happens when a printer is moved. In a facility as large as where I work, it's sometimes very difficult to coordinate migration efforts or new printer installations with all the right people, so communications occasionally get lost in the shuffle. And yet another potential problem: Sometimes I need to have information about a printer that's called back into action after being set aside in storage for a long period of time.

As you can see, a print queue history in my environment can be a vital resource. With as many printers and queues as we have and with possible changes being made at any time by multiple individuals, I've found that it's beneficial for me to capture printer information daily. If you work at a smaller site, you probably need to capture the data only once a week or a few times a month.

Developing the Solution
At first, I devised a solution for capturing my print queue data with a simple Windows Management Instrumentation (WMI) VBScript that gathered information from all print servers and wrote that data to a Microsoft Excel spreadsheet. This solution worked fairly well for tracking down changes; I could spot differences simply by comparing spreadsheets from different dates. But eventually I found that visually searching through numerous spreadsheets for changes, or writing macros to do comparisons, was tedious and inefficient.

Ultimately I modified my script slightly so that in addition to writing data to Excel, it wrote and saved the printer data to an ADO database as an XML file. With the data in a database, I could easily write a script that compared the data from different days in a fraction of the time it would take to do so manually.

The first of my scripts, the one I run daily, is called PrinterInfo.vbs; Listing 1 shows the code, and you can download the script by clicking the Download the Code Here button at the top of this page. When you run this script, it displays a spreadsheet that consists of a worksheet tab for each print server, an Error worksheet, and a Summary worksheet. The Summary sheet shows all of the print servers by name and the total number of print queues on each one. The summary also shows the number of printer errors detected on all servers combined as well as any differences between the last run of the script and the current run, such as new or deleted printers, driver changes, location or comment changes, and changes on other pertinent fields. Each of the printer worksheet tabs contains print queue information for that particular print server, and the Errors worksheet houses printer errors detected for all print servers. The first time you execute this script, of course, you won't have comparison information reported in the spreadsheet.

What PrinterInfo.vbs Does
As I mentioned, the PrinterInfo script uses WMI to gather the printer information and stores that data in an XML-based database using ADO. Here’s a list of the fields that it acquires data on:

  • PrintShare (a concatenation of PrinterServer and Printer ShareName)
  • PortName
  • DriverName
  • PrinterName
  • Location (as entered in the printer properties Location field)
  • DetectedErrorState (see the DetErr array at callout B in Listing 1 for possible errors)
  • Status (i.e., error or OK)
  • Comment (as entered in the printer properties Comment field)
  • PrintProcessor
  • PrinterStatus (see the PrtStatus array at callout B for a list of statuses)
  • BiDirectionalEnabled (i.e., true or false)
  • PrinterState (e.g., Paper Jam, Out of Paper; see Function PrnState in Listing 1 for a complete list)

After writing all the current data to the spreadsheet and to the database, the script opens the database from the previous run of the script (if one exists) and does an item-to-item comparison, writing any differences to the spreadsheet. First, the script compares the latest database with the previous one to find new entries, then it compares the previous to the latest to find items that might have been deleted. Finally, it compares the printers that exist in both databases to see if significant fields differ; any differences between fields are considered changed items. Figure 1 shows a sample Summary sheet.

How PrinterInfo.vbs Does What It Does
With the information from PrinterInfo.vbs readily at hand, I have a quick and accurate view of changes that took place—a history of all of my printers for a particular day and an error listing that will help me pinpoint printers that need attention. The process behind the script is relatively straightforward:

  1. Set the DBPath variable to an existing folder that will house the databases, as the code at callout A in Listing 1 shows. Note that you'll need to change the path in the script to match your environment.
  2. Create an array consisting of your print server names, as shown in callout B. Be sure to modify this in the script to match your environment; simply enter all of your print servers into the array string. You'll also notice right below the print server array that I’ve set up arrays to accommodate the printer properties PrinterStatus and DetectedErrorState, which return only numbers; these numbers are converted to associated textual values via function calls that use these arrays before writing the data to Excel and the database.
  3. Set up database filename variables, as callout C shows.
  4. Create an ADO disconnected recordset with printer-related fields, as the code at callout D shows.
  5. Cycle through the print servers and use WMI to collect data and write it to Excel and the database, which is what callout E shows.
  6. Produce the Summary worksheet showing print queue totals and error totals, as shown at callout F.
  7. Rename what was PreviousPrnList.xml to ArcPrnListmm-dd-yyyy hhmm-ss, and rename what was NewestPrnList.xml to PreviousPrnList.xml, which you can see in the code at callout G.
  8. Compare databases and write any differences to the Summary worksheet, as callout H shows.
  9. Save the current ADO disconnected recordset as NewestPrnList.xml, as callout I shows.

In callout C, you’ll notice that in preparation of naming an archive database file, I manipulate the DateLastModified property of the PreviousPrnList file with the functions ZeroData and MilitaryTime. This step is necessary to make accommodations for my second script, which lets you do selective database comparisons. I have to ensure that archive filenames don't exceed 31 characters, which is the limit Excel places on worksheet names. The second script, an HTML Application (HTA) called PrinterInfoCompare.hta, names worksheet tabs with the XML database filenames (minus the file extension), which makes finding specific worksheets easy. The archive filenames are in the format ArcPrnListmm-dd-yyyy hhmm-ss.xml, which satisfies the length limitation for worksheet names.

The hhmm-ss part of the filename is a military time format, which uses fewer characters, but with a hyphen substituted for what should be a colon because a colon character can't be used in a filename. The mm-dd-yyyy segment of the filename also undergoes modification. This date (as well as the time) originates from the DateLastModified timestamp of the original printer database file and doesn't usually contain leading zeros. To get the files to sort correctly for the HTA script, it's necessary to add leading zeros; so 7/7/2008 would end up as 07/07/2008, for example. You’ll find the two functions, ZeroDate and MilitaryTime, near the bottom of the first script.

Data Listings and Comparisons with PrinterInfoCompare.hta
As time goes by, you’ll undoubtedly accumulate many archive files, and there will come a time when you need to determine what changes took place between certain dates. With these printer history databases available, you’ll find that you now have the ability to see what changed between different points in time. That’s where PrinterInfoCompare.hta comes into play: It provides a simple and easy-to-use GUI for performing such comparisons. You can download the script by clicking the Download the Code Here button at the top of this page. As Figure 2 shows, the interface lets you

  • enter a path or browse to the folder where the databases reside
  • select a database to get a printer data listing from
  • select single or multiple databases to compare against a specific database

You might have noticed in the title bar of the application window in Figure 2 that you can get help by pressing the F1 key. Each of the input elements on the GUI screen has its own context-sensitive Help file built into the application. You simply place the cursor into an area on the screen and press F1 to get information on that specific area. Figure 3 shows you what the Help pop-up looks like; this pop-up is also presented to users when the application is first launched. The Add Sheets check box in the GUI lets you generate multiple listings to the same Excel workbook; clearing this check box creates individual printer information and comparative listings in separate Excel workbooks.

When you select an item from the upper list box and click the ProcessSelected button, you'll be presented with a spreadsheet showing all of the printer information available for the selected database. This output is the same as what you’d see when you ran the daily PrinterInfo.vbs script. You can produce one of these reports for as many items in the upper list box as you like. If you select an item that you’ve previously processed, the script opens the workbook to that particular worksheet instead of creating a new worksheet.

To compare one database with another, you must first select the Compare check box to enable the bottom list box. Select one item from the top list box, which will be your source for comparison, and one or more databases from the bottom list box to compare to the source. Click the ProcessSelected button to run the comparison. When the process is complete, you'll get the results in an Excel worksheet that lists any differences detected. If the databases have no differences, you'll see an entry in the spreadsheet saying "Databases Match." You’ll also notice that each of the selected databases has its own populated worksheet tab so you can open that worksheet and review what that particular database has in it for printer information.

The comparison results are presented on the Compare worksheet. Information about the database selected in the upper list box is shown in the left column of the worksheet, and information about the database you're comparing it to will be in the middle column; the last column contains driver information. When you compare the source database with multiple comparison databases, you'll need to scroll down through the Compare worksheet because comparisons are done one after the other. As Figure 4 shows, the filenames of the source and comparison databases appear above each comparison listing.

In my testing of this HTA application, I found that I could have many kinds of single listings and comparisons within the same workbook, but I don't recommend doing that because it can become a bit overwhelming. I suggest keeping your workbooks focused on just a few comparisons. You can easily create separate workbooks within the application simply by clearing the Add Sheets check box to create a new workbook for the next listing or comparison you run. If you need to add several listings to that new workbook, just select the Add Sheets check box again before running those additional items.

Tools to Make Your Job Easier
The PrinterInfoCompare.hta script contains quite a bit of code. Rather than stepping you through all of it here, you can refer to a previous article that I wrote, "How to Easily View the Extended Properties of Files," that includes a script that performs many of the same routines as this one and contains detailed information on the code behind the process.

I certainly hope that you find these two scripting utilities useful. They should make the job of troubleshooting printer problems a little easier and help you keep track of changes and maintain printer information history.

Listing 1: PrinterInfo.vbs On Error Resume Next
Public Row,Col,XL,ErrRow,ErrCol,PS,j
Col = 1 : Row = 1 : ErrRow = 1 : ErrCol = 1
TotalPrinters = 0 : TotalErrors = 0 : TotSrvQ = 0
Dim SrvArray()
j = 0 : CloseDRS2 = False  
' Begin Callout A
DBPath = "C:\Scripts\PrinterInfo\"
'End Callout A  
' Begin Callout B
PServers = Array("PServer1","PServer2","PServer3","PServer4",
 "PServer5")
Redim Preserve SrvArray(ubound(PServers),1)
PrtStatus = Array("Element0Ignore","Other","Unknown","Idle",
 "Printing","Warmup","Stopped printing","Offline")
DetErr = Array("Unknown","Other","No Error","Low Paper","No Paper",
 "Low Toner","No Toner","Door Open", _
 "Jammed","Offline","Service Requested","Output Bin Full")
' End Callout B
Set fso = CreateObject("Scripting.FileSystemObject") If Not fso.FolderExists(DBPath) Then
 strMessage = "The Folder " & DBPath & " does not exist!  Please
   create the folder and try again."
 strScriptName = "Data Folder Missing"
 CreateObject("WScript.Shell").Popup strMessage,45,strScriptName,
   vbInformation
 Wscript.Quit
End If  
' Begin Callout C
PreviousPrnList = DBPath & "PreviousPrnList.xml"
NewestPrnList = DBPath & "NewestPrnList.xml"
If fso.FileExists(PreviousPrnList) Then
 set f = fso.GetFile(PreviousPrnList)
 DateTime = Replace(ZeroDate(f.DateLastModified),"/","-")
   'cannot use datecreated as it never changes
 DateTime = MilitaryTime(DateTime)
 DateTime = Replace(DateTime,":","-")
 ArchivePrnList = DBPath & "ArcPrnList" & DateTime & ".xml"
End If
' End Callout C
strMessage = "Spreadsheet will open when process is Done"
strScriptName = "Get Printer Info"
CreateObject("WScript.Shell").Popup strMessage,2,strScriptName,
 vbInformation
'*** Setup Excel worksheet called Errors
Tabcolor = 3
Set XL = CreateObject("Excel.Application")
XL.Workbooks.Add XL.Sheets.Add.Name = "Errors"
XL.Cells(ErrRow,ErrCol).Value = "PrintShare"  : ErrCol = ErrCol + 1
XL.Cells(ErrRow,ErrCol).Value = "ErrorState" : ErrCol = ErrCol + 1
XL.Cells(ErrRow,ErrCol).Value = "Status"     : ErrCol = ErrCol + 1
XL.Cells(ErrRow,ErrCol).Value = "PortName"   : ErrCol = ErrCol + 1
XL.Cells(ErrRow,ErrCol).Value = "DriverName" : ErrCol = ErrCol + 1
XL.Cells(ErrRow,ErrCol).Value = "Location"   : ErrCol = ErrCol + 1
XL.Cells(ErrRow,ErrCol).Value = "Comment"    : ErrCol = ErrCol + 1
XL.Cells(ErrRow,ErrCol).Value = "State"      : ErrCol = ErrCol + 1
XL.Rows("2:2").Select
XL.ActiveWindow.FreezePanes = True
XL.Range("A2").Select
XL.ActiveWorkbook.Sheets("Errors").Tab.ColorIndex = TabColor
ErrCol=1 : ErrRow = 2
'*** Create Disconnected Recordset ' Begin Callout D
Const adFldIsNullable = 32
Set DRS = Nothing
Set DRS = CreateObject("ADODB.Recordset")
DRS.Fields.Append "PrintServer",201,256,adFldIsNullable
DRS.Fields.Append "ShareName",201,256,adFldIsNullable
DRS.Fields.Append "PrintShare",201,256,adFldIsNullable
DRS.Fields.Append "PortName",201,256,adFldIsNullable
DRS.Fields.Append "DriverName",201,256,adFldIsNullable
DRS.Fields.Append "PrinterName",201,256,adFldIsNullable
DRS.Fields.Append "Location",201,256,adFldIsNullable
DRS.Fields.Append "ErrorState",201,256,adFldIsNullable
DRS.Fields.Append "Status",201,256,adFldIsNullable
DRS.Fields.Append "Comment",201,256,adFldIsNullable
DRS.Fields.Append "PrintProcessor",201,256,adFldIsNullable
DRS.Fields.Append "PrinterStatus",201,256,adFldIsNullable
DRS.Fields.Append "BiDirectionalEnabled",201,256,adFldIsNullable
DRS.Fields.Append "PrinterState",201,256,adFldIsNullable
DRS.Open
' End Callout D  
'*** Collect printer info ' Begin Callout E
For Each PS In PServers
 TotSrvQ = 0
 strComputer = PS
 Set objWMIService = nothing
 Set colInstalledPrinters = nothing
 Set objWMIService = GetObject("winmgmts:" &
   "\{impersonationLevel=impersonate\}!\\" & strComputer & "\root\cimv2")
 Set colInstalledPrinters = objWMIService.ExecQuery
   ("Select * from Win32_Printer Where ShareName IS NOT NULL")
  '*** Check for bad Printserver name or no shared printers
 If colInstalledPrinters.count < 1 Or Err.Number <> 0 Then
   SrvArray(j,0) = PS
   SrvArray(j,1) = "No Shared Printers or " & PS & " Not Found"
   j = j + 1
   Err.Clear
 Else
   '*** create worksheet for printer
   Tabcolor = Tabcolor + 1
   XL.Sheets.Add.name = PS
   XL.ActiveWorkbook.Sheets(PS).Tab.ColorIndex = TabColor
   '*** Create Excel Headers
   Row = 1 : Col = 1
   XL.Cells(Row,Col).Value = "PrintShare"           : Col = Col + 1
   XL.Cells(Row,Col).Value = "PortName"            : Col = Col + 1
   XL.Cells(Row,Col).Value = "DriverName"           : Col = Col + 1
   XL.Cells(Row,Col).Value = "PrinterName"          : Col = Col + 1
   XL.Cells(Row,Col).Value = "Location"             : Col = Col + 1
   XL.Cells(Row,Col).Value = "ErrorState"           : Col = Col + 1
   XL.Cells(Row,Col).Value = "Status"               : Col = Col + 1
   XL.Cells(Row,Col).Value = "Comment"              : Col = Col + 1
   XL.Cells(Row,Col).Value = "PrintProcessor"       : Col = Col + 1
   XL.Cells(Row,Col).Value = "PrinterStatus"        : Col = Col + 1
   XL.Cells(Row,Col).Value = "BiDirectionalEnabled" : Col = Col + 1
   XL.Cells(Row,Col).Value = "PrinterState"         : Col = Col + 1
   Row = Row + 1 : Col = 1
    '*** Get printer info and write to Excel and Database
   For Each objPrinter In colInstalledPrinters
     DRS.AddNew
   DRS("PrintServer") = objPrinter.SystemName
   DRS("ShareName") = objPrinter.ShareName
     XL.Cells(Row,Col).Value = "\\" & objPrinter.SystemName &
       "\" & objPrinter.ShareName : Col = Col + 1
   DRS("PrintShare") = "\\" & objPrinter.SystemName &
     "\" & objPrinter.ShareName
   XL.Cells(Row,Col).Value = objPrinter.PortName       : Col = Col + 1
   DRS("PortName") = objPrinter.PortName
   XL.Cells(Row,Col).Value = objPrinter.DriverName     : Col = Col + 1
   DRS("DriverName") = objPrinter.DriverName
   XL.Cells(Row,Col).Value = objPrinter.Name           : Col = Col + 1
   DRS("PrinterName") = objPrinter.Name
   XL.Cells(Row,Col).Value = objPrinter.Location       : Col = Col + 1
   DRS("Location") = objPrinter.Location
   XL.Cells(Row,Col).Value = objPrinter.DetectedErrorState &
     " " & DetErr(objPrinter.DetectedErrorState): Col = Col + 1
   DRS("ErrorState") = objPrinter.DetectedErrorState & " " &
     DetErr(objPrinter.DetectedErrorState)
   XL.Cells(Row,Col).Value = objPrinter.Status         : Col = Col + 1
   DRS("Status") = objPrinter.Status
   XL.Cells(Row,Col).Value = objPrinter.Comment        : Col = Col + 1
   DRS("Comment") = objPrinter.Comment
   XL.Cells(Row,Col).Value = objPrinter.PrintProcessor : Col = Col + 1
   DRS("PrintProcessor") = objPrinter.PrintProcessor
   XL.Cells(Row,Col).Value = objprinter.PrinterStatus & " " &
     PrtStatus(objPrinter.PrinterStatus) : Col = Col + 1
   DRS("PrinterStatus") = objPrinter.PrinterStatus & " " &
     PrtStatus(objPrinter.PrinterStatus)
   XL.Cells(Row,Col).Value = objprinter.Enablebidi     : Col = Col + 1
   DRS("BiDirectionalEnabled") = objPrinter.Enablebidi
   XL.Cells(Row,Col).Value = objprinter.PrinterState & " " &
     PrnState(objprinter.PrinterState)
   DRS("PrinterState") = objprinter.PrinterState & " " &
     PrnState(objprinter.PrinterState)
   Col = Col + 1
   If Trim(Ucase(objPrinter.Status))  = "ERROR" _
     Or Trim(Ucase(objPrinter.Status)) = "DEGRADED" _
     Or Trim(Ucase(objPrinter.Status)) = "PRED FAIL" _
     Or Trim(Ucase(objPrinter.Status)) = "STARTING" _
     Or Trim(Ucase(objPrinter.Status)) = "STOPPING" _
     Or Trim(Ucase(objPrinter.Status)) = "SERVICE" _
     Or Trim(Ucase(objPrinter.Status)) = "STRESSED" _
     Or Trim(Ucase(objPrinter.Status)) = "NONRECOVER" _
     Or Trim(Ucase(objPrinter.Status)) = "NO CONTACT" _
     Or Trim(Ucase(objPrinter.Status)) = "LOST COMM" _
     Or (PrinterState <> 0 AND PrinterState <> 1024 AND
       PrinterState <> 131072) Then
    WriteErr
    TotalErrors = TotalErrors + 1
    End If
    Row = Row + 1 : Col = 1
    TotalPrinters = TotalPrinters + 1
    TotSrvQ = TotSrvQ + 1
    DRS.Update
    Next
 ' End Callout E
    SrvArray(j,0) = PS
   SrvArray(j,1) = TotSrvQ
   j = j + 1
   TotSrvQ = 0
    XL.Rows("2:2").Select
   XL.ActiveWindow.FreezePanes = True
   XL.Range("A2").Select
   XL.Cells.EntireColumn.AutoFit
   XL.Cells.Select
   XL.Selection.AutoFilter
   XL.Range("A2").Select
   Row = 1 : Col = 1
 End If
Next
XL.Cells.EntireColumn.AutoFit
Set objWMIService = nothing
Set colInstalledPrinters = nothing
'*** Create a Summary Worksheet in Workbook ' Begin Callout F
XL.Sheets.Add.Name = "Summary"
Row = 1 : Col = 1
For y = 0 to ubound(srvarray)
 XL.Cells(Row,Col).Value = srvarray(y,0)  : Col = Col + 1
 XL.Cells(Row,Col).Value = srvarray(y,1)  : Col = Col + 1
 XL.Cells(Row,Col).Value = "Print Queues" : Row = Row + 1 : Col = 1
Next
Row = Row + 1
XL.Cells(Row,Col).Value = "Total Print Queues" : Col = Col + 1
XL.Cells(Row,Col).Value = TotalPrinters        : Row = Row + 1 : Col = 1
XL.Cells(Row,Col).Value = "Total Errors"       : Col = Col + 1
XL.Cells(Row,Col).Value = TotalErrors          : Row = Row + 1 : Col = 1
' End Callout F
Listing 1: PrinterInfo.vbs, cont.
' Begin Callout G
If fso.FileExists(NewestPrnList) Then
 If fso.FileExists(PreviousPrnList) Then
  fso.MoveFile PreviousPrnList,ArchivePrnList
 End If
 fso.MoveFile NewestPrnList,PreviousPrnList
End If
' End Callout G
' Begin Callout H
'Find New
If fso.FileExists(PreviousPrnList) Then
 Set DRS2 = CreateObject("ADOR.Recordset")
 DRS2.Open PreviousPrnList
 Row = Row + 1
 CloseDRS2 = True
 If DRS.RecordCount > 0 Then
  DRS.MoveFirst
  Do while Not DRS.EOF
   sn = DRS.Fields.Item("PrintShare")
   If DRS2.RecordCount > 0 Then
    DRS2.MoveFirst
   End If
   DRS2.Find("PrintShare = '" & Replace(sn,"'","'") & "'")
    If DRS2.EOF Then
     XL.Cells(Row,Col).Value = sn                     : Col = Col + 1
     XL.Cells(Row,Col).Value = "Possibly New"         : Col = Col + 1
     XL.Cells(Row,Col).Value = "Not in Previous List"
       : Row = Row + 1 : Col = 1
    End If
   DRS.MoveNext  
  Loop
 End If

'Find Removed
 If DRS.RecordCount > 0 Then
  DRS.MoveFirst
 End If
 If DRS2.RecordCount > 0 Then
  DRS2.MoveFirst
 End If
 Do while Not DRS2.EOF
  sn = DRS2.Fields.Item("PrintShare")
  If DRS.RecordCount > 0 Then
   DRS.MoveFirst
  End If
  DRS.Find("PrintShare = '" & Replace(sn,"'","'") & "'")
  If DRS.EOF Then
   XL.Cells(Row,Col).Value = sn
     : Col = Col + 1
   XL.Cells(Row,Col).Value = "Possibly Deleted."
     : Col = Col + 1
   XL.Cells(Row,Col).Value = "In Previous List but
     not in Most Recent List" : Row = Row + 1 : Col = 1
  End If
  DRS2.MoveNext  
 Loop

'Changes
 If DRS.RecordCount > 0 Then
  DRS.MoveFirst
 End If
 If DRS2.RecordCount > 0 Then
  DRS2.MoveFirst
 End If
 Do while Not DRS.EOF
  sn = DRS.Fields.Item("PrintShare")
  If DRS2.RecordCount > 0 Then
   DRS2.MoveFirst
  End If
  DRS2.Find("PrintShare = '" & Replace(sn,"'","'") & "'")
  If Not DRS2.EOF Then
  'compare significant fields
   New1 = DRS.Fields.Item("PortName")
   New2 = DRS.Fields.Item("DriverName")
   New3 = DRS.Fields.Item("PrinterName")
   New4 = DRS.Fields.Item("Location")
   New5 = DRS.Fields.Item("Comment")
   New6 = DRS.Fields.Item("PrintProcessor")
   New7 = DRS.Fields.Item("BiDirectionalEnabled")
   Old1 = DRS2.Fields.Item("PortName")
   Old2 = DRS2.Fields.Item("DriverName")
   Old3 = DRS2.Fields.Item("PrinterName")
   Old4 = DRS2.Fields.Item("Location")
   Old5 = DRS2.Fields.Item("Comment")
   Old6 = DRS2.Fields.Item("PrintProcessor")
   Old7 = DRS2.Fields.Item("BiDirectionalEnabled")
   If (New1 <> Old1) OR (New2 <> Old2) OR (New3 <> Old3) OR (New4 <> Old4) OR _
    (New5 <> Old5) OR (New6 <> Old6) OR (New7 <> Old7) Then
    Col = 1
    XL.Cells(Row,Col).Value = "Changed"     : Col = Col + 1
    XL.Cells(Row,Col).Value = "PortName"    : Col = Col + 1
    XL.Cells(Row,Col).Value = "DriverName"  : Col = Col + 1
    XL.Cells(Row,Col).Value = "PrinterName" : Col = Col + 1
    XL.Cells(Row,Col).Value = "Location"    : Col = Col + 1
    XL.Cells(Row,Col).Value = "Comment"     : Col = Col + 1
    XL.Cells(Row,Col).Value = "PrintProcessor"       : Col = Col + 1
    XL.Cells(Row,Col).Value = "BiDirectionalEnabled" : Row = Row + 1 : Col = 1
    XL.Cells(Row,Col).Value = sn   : Col = Col + 1
    XL.Cells(Row,Col).Value = New1 : Col = Col + 1
    XL.Cells(Row,Col).Value = New2 : Col = Col + 1
    XL.Cells(Row,Col).Value = New3 : Col = Col + 1
    XL.Cells(Row,Col).Value = New4 : Col = Col + 1
    XL.Cells(Row,Col).Value = New5 : Col = Col + 1
    XL.Cells(Row,Col).Value = New6 : Col = Col + 1
    XL.Cells(Row,Col).Value = New7 : Row = Row + 1 : Col = 1
    XL.Cells(Row,Col).Value = sn   : Col = Col + 1
    XL.Cells(Row,Col).Value = old1 : Col = Col + 1
    XL.Cells(Row,Col).Value = old2 : Col = Col + 1
    XL.Cells(Row,Col).Value = old3 : Col = Col + 1
    XL.Cells(Row,Col).Value = old4 : Col = Col + 1
    XL.Cells(Row,Col).Value = old5 : Col = Col + 1
    XL.Cells(Row,Col).Value = old6 : Col = Col + 1
    XL.Cells(Row,Col).Value = old7 : Col = Col + 1 : Row = Row + 1
   End If
  End If
  DRS.MoveNext  
 Loop
End If
' End Callout H
 
XL.Cells.EntireColumn.AutoFit
XL.Range("A1").Select
' Begin Callout I
Const adPersistXML = 1
DRS.Save NewestPrnList,adPersistXML
DRS.Close
' End Callout I

If CloseDRS2 Then
 DRS2.Close
End If

XL.Visible = TRUE
Set XL = nothing

strMessage = "Done"
strScriptName = "Get Printer Info"
CreateObject("WScript.Shell").Popup strMessage,2,strScriptName,vbInformation

Sub WriteErr
 On Error Resume Next
 XL.Sheets("Errors").Select
 XL.Cells(ErrRow,ErrCol).Value = "\\" & objPrinter.SystemName &
   "\" & objPrinter.ShareName : ErrCol = ErrCol + 1
 XL.Cells(ErrRow,ErrCol).Value = DetErr(objPrinter.DetectedErrorState)
   : ErrCol = ErrCol + 1
 XL.Cells(ErrRow,ErrCol).Value = objPrinter.Status : ErrCol = ErrCol + 1
 XL.Cells(ErrRow,ErrCol).Value = objPrinter.PortName : ErrCol = ErrCol + 1
 XL.Cells(ErrRow,ErrCol).Value = objPrinter.DriverName : ErrCol = ErrCol + 1
 XL.Cells(ErrRow,ErrCol).Value = objPrinter.Location : ErrCol = ErrCol + 1
 XL.Cells(ErrRow,ErrCol).Value = objPrinter.Comment : ErrCol = ErrCol + 1
 XL.Cells(ErrRow,ErrCol).Value = objprinter.PrinterState & " " &
   PrnState(objprinter.PrinterState) : ErrCol = ErrCol + 1
 ErrRow = ErrRow + 1 : ErrCol = 1
 XL.Cells.EntireColumn.AutoFit
 XL.Sheets(PS).Select
End Sub
Function PrnState(pstate)
 Select Case pstate
  Case "0" CurState        = "Online"
  Case "1" CurState        = "Paused"
  Case "2" CurState        = "Pending Deletion"
  Case "3" CurState        = "Error"
  Case "4" CurState        = "Paper Jam"
  Case "5" CurState        = "Paper Out"
  Case "6" CurState        = "Manual Feed"
  Case "7" CurState        = "Paper Problem"
  Case "8" CurState        = "Offline"
  Case "16" CurState       = "No Paper"
  Case "128" CurState      = "Offline"
  Case "256" CurState      = "IO Active"
  Case "512" CurState      = "Busy"
  Case "1024" CurState     = "Printing"
  Case "1028" CurState = "Disconnected-Offline"
  Case "1034" CurState     = "Jammed"
  Case "1042" CurState     = "Add Media"
  Case "2048" CurState     = "Output Bin Full"
  Case "4096" CurState     = "Not Available"
  Case "8192" CurState     = "Waiting"
  Case "6384" CurState     = "Processing"
  Case "32768" CurState    = "Initializing"
  Case "65536" CurState    = "Warming Up"
  Case "131072" CurState   = "Toner Low"
  Case "262144" CurState   = "No Toner"
  Case "524288" CurState   = "Page Punt"
  Case "263170" CurState   = "OutPut Bin Full"
  Case "1048576" CurState  = "User Intervention"
  Case "2097152" CurState  = "Out of Memory"
  Case "4194304" CurState  = "Door Open"
  Case "8388608" CurState  = "Server Unknown"
  Case "16777216" CurState = "Power Save"
 End Select
 PrnState = CurState
End Function
Function ZeroDate(dDate)
 '*** convert dates like 7/7/2008 to 07/07/2008
 '*** These are used in the Archive filenames and this
 '*** allows them to be sorted properly when displayed
 '*** in PrinterInfoCompare.HTA
 If Instr(1,Left(dDate,2),"/") <> 0 Then
  dDate = "0" & dDate
 End If
 For k = 1 to 9
  dDate = Replace(dDate,"/" & k & "/","/0" & k & "/")
 Next
 ZeroDate = dDate
End Function
Function MilitaryTime(dDate)
 '*****************************************************
 '*** Convert time portion of DateTime to Military Time
 '*** ex. 12/19/2008 1:07:54 PM would be converted
 '*** to  12/19/2008 1307:54
 '*****************************************************
 DTarray = Split(dDate," ")
 timestr = DTarray(1)
 tarray = Split(timestr,":")
 Select Case DTarray(2) = "PM"
  Case True
   If tarray(0) < 12 Then
    Milhr = tarray(0) + 12
   Else
    Milhr = tarray(0)
   End If
  Case False
   If tarray(0) < 10 Then
    Milhr = "0" & tarray(0)
   ElseIf tarray(0) = 12 Then
    Milhr = "00"
   Else
    Milhr = tarray(0)
   End If
 End Select
 MilitaryTime = DTarray(0) & " " & Milhr & tarray(1) & ":" & tarray(2)
End Function