How to: Remotely Survey the Status of Printers

At any given time at medium and large companies there may be multiple printers that are not working due to a paper jam or being out of paper or some other problematic situation. The rich set of printer properties exposed in the APIs of Microsoft .NET Framework provide a means for performing a rapid survey of the states of printers.

Example

The major steps for creating this kind of utility are as follows.

  1. Obtain a list of all print servers.

  2. Loop through the servers to query their print queues.

  3. Within each pass of the server loop, loop through all the server's queues and read each property that might indicate that the queue is not currently working.

The code below is a series of snippets. For simplicity, this example assumes that there is a CRLF-delimited list of print servers. The variable fileOfPrintServers is a StreamReader object for this file. Since each server name is on its own line, any call of ReadLine gets the name of the next server and moves the StreamReader's cursor to the beginning of the next line.

Within the outer loop, the code creates a PrintServer object for the latest print server and specifies that the application is to have administrative rights to the server.

Note

If there are a lot of servers, you can improve performance by using the PrintServer(String, String[], PrintSystemDesiredAccess) constructors that only initialize the properties you are going to need.

The example then uses GetPrintQueues to create a collection of all of the server's queues and begins to loop through them. This inner loop contains a branching structure corresponding to the two ways of checking a printer's status:

This example demonstrates both methods, so the user was previously prompted as to which method to use and responded with "y" if he or she wanted to use the flags of the QueueStatus property. See below for the details of the two methods.

Finally, the results are presented to the user.

            ' Survey queue status for every queue on every print server
            Dim line As String
            Dim statusReport As String = vbLf & vbLf & "Any problem states are indicated below:" & vbLf & vbLf
            line = fileOfPrintServers.ReadLine()
            Do While line IsNot Nothing
                 Dim myPS As New PrintServer(line, PrintSystemDesiredAccess.AdministrateServer)
                 Dim myPrintQueues As PrintQueueCollection = myPS.GetPrintQueues()
                 statusReport = statusReport & vbLf & line
                 For Each pq As PrintQueue In myPrintQueues
                     pq.Refresh()
                     statusReport = statusReport & vbLf & vbTab & pq.Name & ":"
                     If useAttributesResponse = "y" Then
                         TroubleSpotter.SpotTroubleUsingQueueAttributes(statusReport, pq)
                         ' TroubleSpotter class is defined in the complete example.
                     Else
                         TroubleSpotter.SpotTroubleUsingProperties(statusReport, pq)
                     End If

                 Next pq ' end for each print queue

                line = fileOfPrintServers.ReadLine()
            Loop ' end while list of print servers is not yet exhausted

            fileOfPrintServers.Close()
            Console.WriteLine(statusReport)
            Console.WriteLine(vbLf & "Press Return to continue.")
            Console.ReadLine()

// Survey queue status for every queue on every print server
String line;
String statusReport = "\n\nAny problem states are indicated below:\n\n";
while ((line = fileOfPrintServers.ReadLine()) != null)
 {
     PrintServer myPS = new PrintServer(line, PrintSystemDesiredAccess.AdministrateServer);
     PrintQueueCollection myPrintQueues = myPS.GetPrintQueues();
     statusReport = statusReport + "\n" + line;
     foreach (PrintQueue pq in myPrintQueues)
     {
         pq.Refresh();
         statusReport = statusReport + "\n\t" + pq.Name + ":";
         if (useAttributesResponse == "y")
         {
             TroubleSpotter.SpotTroubleUsingQueueAttributes(ref statusReport, pq);
             // TroubleSpotter class is defined in the complete example.
         }
         else
         {
             TroubleSpotter.SpotTroubleUsingProperties(ref statusReport, pq);
         }                 

     }// end for each print queue

 }// end while list of print servers is not yet exhausted

fileOfPrintServers.Close();
Console.WriteLine(statusReport);
Console.WriteLine("\nPress Return to continue.");
Console.ReadLine();

// Survey queue status for every queue on every print server
System::String^ line;
System::String^ statusReport = "\n\nAny problem states are indicated below:\n\n";
while ((line = fileOfPrintServers->ReadLine()) != nullptr)
{
   System::Printing::PrintServer^ myPS = gcnew System::Printing::PrintServer(line, PrintSystemDesiredAccess::AdministrateServer);
   System::Printing::PrintQueueCollection^ myPrintQueues = myPS->GetPrintQueues();
   statusReport = statusReport + "\n" + line;
   for each (System::Printing::PrintQueue^ pq in myPrintQueues)
   {
      pq->Refresh();
      statusReport = statusReport + "\n\t" + pq->Name + ":";
      if (useAttributesResponse == "y")
      {
         TroubleSpotter::SpotTroubleUsingQueueAttributes(statusReport, pq);
         // TroubleSpotter class is defined in the complete example.
      } else
      {
         TroubleSpotter::SpotTroubleUsingProperties(statusReport, pq);
      }
   }
}
fileOfPrintServers->Close();
Console::WriteLine(statusReport);
Console::WriteLine("\nPress Return to continue.");
Console::ReadLine();

To check printer status using the flags of the QueueStatus property, you check each relevant flag to see if it is set. The standard way to see if one bit is set in a set of bit flags is to perform a logical AND operation with the set of flags as one operand and the flag itself as the other. Since the flag itself has only one bit set, the result of the logical AND is that, at most, that same bit is set. To find out whether it is or not, just compare the result of the logical AND with the flag itself. For more information, see PrintQueueStatus, the & Operator (C# Reference), and FlagsAttribute.

For each attribute whose bit is set, the code adds a notice to the final report that will be presented to the user. (The ReportAvailabilityAtThisTime method that is called at the end of the code is discussed below.)

        ' Check for possible trouble states of a printer using the flags of the QueueStatus property
        Friend Shared Sub SpotTroubleUsingQueueAttributes(ByRef statusReport As String, ByVal pq As PrintQueue)
            If (pq.QueueStatus And PrintQueueStatus.PaperProblem) = PrintQueueStatus.PaperProblem Then
                statusReport = statusReport & "Has a paper problem. "
            End If
            If (pq.QueueStatus And PrintQueueStatus.NoToner) = PrintQueueStatus.NoToner Then
                statusReport = statusReport & "Is out of toner. "
            End If
            If (pq.QueueStatus And PrintQueueStatus.DoorOpen) = PrintQueueStatus.DoorOpen Then
                statusReport = statusReport & "Has an open door. "
            End If
            If (pq.QueueStatus And PrintQueueStatus.Error) = PrintQueueStatus.Error Then
                statusReport = statusReport & "Is in an error state. "
            End If
            If (pq.QueueStatus And PrintQueueStatus.NotAvailable) = PrintQueueStatus.NotAvailable Then
                statusReport = statusReport & "Is not available. "
            End If
            If (pq.QueueStatus And PrintQueueStatus.Offline) = PrintQueueStatus.Offline Then
                statusReport = statusReport & "Is off line. "
            End If
            If (pq.QueueStatus And PrintQueueStatus.OutOfMemory) = PrintQueueStatus.OutOfMemory Then
                statusReport = statusReport & "Is out of memory. "
            End If
            If (pq.QueueStatus And PrintQueueStatus.PaperOut) = PrintQueueStatus.PaperOut Then
                statusReport = statusReport & "Is out of paper. "
            End If
            If (pq.QueueStatus And PrintQueueStatus.OutputBinFull) = PrintQueueStatus.OutputBinFull Then
                statusReport = statusReport & "Has a full output bin. "
            End If
            If (pq.QueueStatus And PrintQueueStatus.PaperJam) = PrintQueueStatus.PaperJam Then
                statusReport = statusReport & "Has a paper jam. "
            End If
            If (pq.QueueStatus And PrintQueueStatus.Paused) = PrintQueueStatus.Paused Then
                statusReport = statusReport & "Is paused. "
            End If
            If (pq.QueueStatus And PrintQueueStatus.TonerLow) = PrintQueueStatus.TonerLow Then
                statusReport = statusReport & "Is low on toner. "
            End If
            If (pq.QueueStatus And PrintQueueStatus.UserIntervention) = PrintQueueStatus.UserIntervention Then
                statusReport = statusReport & "Needs user intervention. "
            End If

            ' Check if queue is even available at this time of day
            ' The method below is defined in the complete example.
            ReportAvailabilityAtThisTime(statusReport, pq)
        End Sub
// Check for possible trouble states of a printer using the flags of the QueueStatus property
internal static void SpotTroubleUsingQueueAttributes(ref String statusReport, PrintQueue pq)
{
    if ((pq.QueueStatus & PrintQueueStatus.PaperProblem) == PrintQueueStatus.PaperProblem)
    {
        statusReport = statusReport + "Has a paper problem. ";
    }
    if ((pq.QueueStatus & PrintQueueStatus.NoToner) == PrintQueueStatus.NoToner)
    {
        statusReport = statusReport + "Is out of toner. ";
    }
    if ((pq.QueueStatus & PrintQueueStatus.DoorOpen) == PrintQueueStatus.DoorOpen)
    {
        statusReport = statusReport + "Has an open door. ";
    }
    if ((pq.QueueStatus & PrintQueueStatus.Error) == PrintQueueStatus.Error)
    {
        statusReport = statusReport + "Is in an error state. ";
    }
    if ((pq.QueueStatus & PrintQueueStatus.NotAvailable) == PrintQueueStatus.NotAvailable)
    {
        statusReport = statusReport + "Is not available. ";
    }
    if ((pq.QueueStatus & PrintQueueStatus.Offline) == PrintQueueStatus.Offline)
    {
        statusReport = statusReport + "Is off line. ";
    }
    if ((pq.QueueStatus & PrintQueueStatus.OutOfMemory) == PrintQueueStatus.OutOfMemory)
    {
        statusReport = statusReport + "Is out of memory. ";
    }
    if ((pq.QueueStatus & PrintQueueStatus.PaperOut) == PrintQueueStatus.PaperOut)
    {
        statusReport = statusReport + "Is out of paper. ";
    }
    if ((pq.QueueStatus & PrintQueueStatus.OutputBinFull) == PrintQueueStatus.OutputBinFull)
    {
        statusReport = statusReport + "Has a full output bin. ";
    }
    if ((pq.QueueStatus & PrintQueueStatus.PaperJam) == PrintQueueStatus.PaperJam)
    {
        statusReport = statusReport + "Has a paper jam. ";
    }
    if ((pq.QueueStatus & PrintQueueStatus.Paused) == PrintQueueStatus.Paused)
    {
        statusReport = statusReport + "Is paused. ";
    }
    if ((pq.QueueStatus & PrintQueueStatus.TonerLow) == PrintQueueStatus.TonerLow)
    {
        statusReport = statusReport + "Is low on toner. ";
    }
    if ((pq.QueueStatus & PrintQueueStatus.UserIntervention) == PrintQueueStatus.UserIntervention)
    {
        statusReport = statusReport + "Needs user intervention. ";
    }

    // Check if queue is even available at this time of day
    // The method below is defined in the complete example.
    ReportAvailabilityAtThisTime(ref statusReport, pq);
}
internal: 
   // Check for possible trouble states of a printer using the flags of the QueueStatus property
   static void SpotTroubleUsingQueueAttributes (System::String^% statusReport, System::Printing::PrintQueue^ pq) 
   {
      if ((pq->QueueStatus & PrintQueueStatus::PaperProblem) == PrintQueueStatus::PaperProblem)
      {
         statusReport = statusReport + "Has a paper problem. ";
      }
      if ((pq->QueueStatus & PrintQueueStatus::NoToner) == PrintQueueStatus::NoToner)
      {
         statusReport = statusReport + "Is out of toner. ";
      }
      if ((pq->QueueStatus & PrintQueueStatus::DoorOpen) == PrintQueueStatus::DoorOpen)
      {
         statusReport = statusReport + "Has an open door. ";
      }
      if ((pq->QueueStatus & PrintQueueStatus::Error) == PrintQueueStatus::Error)
      {
         statusReport = statusReport + "Is in an error state. ";
      }
      if ((pq->QueueStatus & PrintQueueStatus::NotAvailable) == PrintQueueStatus::NotAvailable)
      {
         statusReport = statusReport + "Is not available. ";
      }
      if ((pq->QueueStatus & PrintQueueStatus::Offline) == PrintQueueStatus::Offline)
      {
         statusReport = statusReport + "Is off line. ";
      }
      if ((pq->QueueStatus & PrintQueueStatus::OutOfMemory) == PrintQueueStatus::OutOfMemory)
      {
         statusReport = statusReport + "Is out of memory. ";
      }
      if ((pq->QueueStatus & PrintQueueStatus::PaperOut) == PrintQueueStatus::PaperOut)
      {
         statusReport = statusReport + "Is out of paper. ";
      }
      if ((pq->QueueStatus & PrintQueueStatus::OutputBinFull) == PrintQueueStatus::OutputBinFull)
      {
         statusReport = statusReport + "Has a full output bin. ";
      }
      if ((pq->QueueStatus & PrintQueueStatus::PaperJam) == PrintQueueStatus::PaperJam)
      {
         statusReport = statusReport + "Has a paper jam. ";
      }
      if ((pq->QueueStatus & PrintQueueStatus::Paused) == PrintQueueStatus::Paused)
      {
         statusReport = statusReport + "Is paused. ";
      }
      if ((pq->QueueStatus & PrintQueueStatus::TonerLow) == PrintQueueStatus::TonerLow)
      {
         statusReport = statusReport + "Is low on toner. ";
      }
      if ((pq->QueueStatus & PrintQueueStatus::UserIntervention) == PrintQueueStatus::UserIntervention)
      {
         statusReport = statusReport + "Needs user intervention. ";
      }

      // Check if queue is even available at this time of day
      // The method below is defined in the complete example.
      ReportAvailabilityAtThisTime(statusReport, pq);
   };

To check printer status using each property, you simply read each property and add a note to the final report that will be presented to the user if the property is true. (The ReportAvailabilityAtThisTime method that is called at the end of the code is discussed below.)

        ' Check for possible trouble states of a printer using its properties
        Friend Shared Sub SpotTroubleUsingProperties(ByRef statusReport As String, ByVal pq As PrintQueue)
            If pq.HasPaperProblem Then
                statusReport = statusReport & "Has a paper problem. "
            End If
            If Not(pq.HasToner) Then
                statusReport = statusReport & "Is out of toner. "
            End If
            If pq.IsDoorOpened Then
                statusReport = statusReport & "Has an open door. "
            End If
            If pq.IsInError Then
                statusReport = statusReport & "Is in an error state. "
            End If
            If pq.IsNotAvailable Then
                statusReport = statusReport & "Is not available. "
            End If
            If pq.IsOffline Then
                statusReport = statusReport & "Is off line. "
            End If
            If pq.IsOutOfMemory Then
                statusReport = statusReport & "Is out of memory. "
            End If
            If pq.IsOutOfPaper Then
                statusReport = statusReport & "Is out of paper. "
            End If
            If pq.IsOutputBinFull Then
                statusReport = statusReport & "Has a full output bin. "
            End If
            If pq.IsPaperJammed Then
                statusReport = statusReport & "Has a paper jam. "
            End If
            If pq.IsPaused Then
                statusReport = statusReport & "Is paused. "
            End If
            If pq.IsTonerLow Then
                statusReport = statusReport & "Is low on toner. "
            End If
            If pq.NeedUserIntervention Then
                statusReport = statusReport & "Needs user intervention. "
            End If

            ' Check if queue is even available at this time of day
            ' The following method is defined in the complete example.
            ReportAvailabilityAtThisTime(statusReport, pq)

        End Sub 'end SpotTroubleUsingProperties
// Check for possible trouble states of a printer using its properties
internal static void SpotTroubleUsingProperties(ref String statusReport, PrintQueue pq)
{
    if (pq.HasPaperProblem)
    {
        statusReport = statusReport + "Has a paper problem. ";
    }
    if (!(pq.HasToner))
    {
        statusReport = statusReport + "Is out of toner. ";
    }
    if (pq.IsDoorOpened)
    {
        statusReport = statusReport + "Has an open door. ";
    }
    if (pq.IsInError)
    {
        statusReport = statusReport + "Is in an error state. ";
    }
    if (pq.IsNotAvailable)
    {
        statusReport = statusReport + "Is not available. ";
    }
    if (pq.IsOffline)
    {
        statusReport = statusReport + "Is off line. ";
    }
    if (pq.IsOutOfMemory)
    {
        statusReport = statusReport + "Is out of memory. ";
    }
    if (pq.IsOutOfPaper)
    {
        statusReport = statusReport + "Is out of paper. ";
    }
    if (pq.IsOutputBinFull)
    {
        statusReport = statusReport + "Has a full output bin. ";
    }
    if (pq.IsPaperJammed)
    {
        statusReport = statusReport + "Has a paper jam. ";
    }
    if (pq.IsPaused)
    {
        statusReport = statusReport + "Is paused. ";
    }
    if (pq.IsTonerLow)
    {
        statusReport = statusReport + "Is low on toner. ";
    }
    if (pq.NeedUserIntervention)
    {
        statusReport = statusReport + "Needs user intervention. ";
    }

    // Check if queue is even available at this time of day
    // The following method is defined in the complete example.
    ReportAvailabilityAtThisTime(ref statusReport, pq);

}//end SpotTroubleUsingProperties
internal: 
   // Check for possible trouble states of a printer using its properties
   static void SpotTroubleUsingProperties (System::String^% statusReport, System::Printing::PrintQueue^ pq) 
   {
      if (pq->HasPaperProblem)
      {
         statusReport = statusReport + "Has a paper problem. ";
      }
      if (!(pq->HasToner))
      {
         statusReport = statusReport + "Is out of toner. ";
      }
      if (pq->IsDoorOpened)
      {
         statusReport = statusReport + "Has an open door. ";
      }
      if (pq->IsInError)
      {
         statusReport = statusReport + "Is in an error state. ";
      }
      if (pq->IsNotAvailable)
      {
         statusReport = statusReport + "Is not available. ";
      }
      if (pq->IsOffline)
      {
         statusReport = statusReport + "Is off line. ";
      }
      if (pq->IsOutOfMemory)
      {
         statusReport = statusReport + "Is out of memory. ";
      }
      if (pq->IsOutOfPaper)
      {
         statusReport = statusReport + "Is out of paper. ";
      }
      if (pq->IsOutputBinFull)
      {
         statusReport = statusReport + "Has a full output bin. ";
      }
      if (pq->IsPaperJammed)
      {
         statusReport = statusReport + "Has a paper jam. ";
      }
      if (pq->IsPaused)
      {
         statusReport = statusReport + "Is paused. ";
      }
      if (pq->IsTonerLow)
      {
         statusReport = statusReport + "Is low on toner. ";
      }
      if (pq->NeedUserIntervention)
      {
         statusReport = statusReport + "Needs user intervention. ";
      }

      // Check if queue is even available at this time of day
      // The following method is defined in the complete example.
      ReportAvailabilityAtThisTime(statusReport, pq);
   };

The ReportAvailabilityAtThisTime method was created in case you need to determine if the queue is available at the current time of day.

The method will do nothing if the StartTimeOfDay and UntilTimeOfDay properties are equal; because in that case the printer is available at all times. If they are different, the method gets the current time which then has to be converted into total minutes past midnight because the StartTimeOfDay and UntilTimeOfDay properties are Int32s representing minutes-after-midnight, not DateTime objects. Finally, the method checks to see if the current time is between the start and "until" times.

        Private Shared Sub ReportAvailabilityAtThisTime(ByRef statusReport As String, ByVal pq As PrintQueue)
            If pq.StartTimeOfDay <> pq.UntilTimeOfDay Then ' If the printer is not available 24 hours a day
        Dim utcNow As Date = Date.UtcNow
        Dim utcNowAsMinutesAfterMidnight As Int32 = (utcNow.TimeOfDay.Hours * 60) + utcNow.TimeOfDay.Minutes

                ' If now is not within the range of available times . . .
                If Not((pq.StartTimeOfDay < utcNowAsMinutesAfterMidnight) AndAlso (utcNowAsMinutesAfterMidnight < pq.UntilTimeOfDay)) Then
                    statusReport = statusReport & " Is not available at this time of day. "
                End If
            End If
        End Sub
        private static void ReportAvailabilityAtThisTime(ref String statusReport, PrintQueue pq)
        {
            if (pq.StartTimeOfDay != pq.UntilTimeOfDay) // If the printer is not available 24 hours a day
            {
        DateTime utcNow = DateTime.UtcNow;
        Int32 utcNowAsMinutesAfterMidnight = (utcNow.TimeOfDay.Hours * 60) + utcNow.TimeOfDay.Minutes;

                // If now is not within the range of available times . . .
                if (!((pq.StartTimeOfDay < utcNowAsMinutesAfterMidnight) 
                   &&
                   (utcNowAsMinutesAfterMidnight < pq.UntilTimeOfDay)))
                {
                    statusReport = statusReport + " Is not available at this time of day. ";
                }
            }
        }
private: 
   static void ReportAvailabilityAtThisTime (System::String^% statusReport, System::Printing::PrintQueue^ pq) 
   {
      if (pq->StartTimeOfDay != pq->UntilTimeOfDay)
      {
         System::DateTime utcNow = DateTime::UtcNow;
         System::Int32 utcNowAsMinutesAfterMidnight = (utcNow.TimeOfDay.Hours * 60) + utcNow.TimeOfDay.Minutes;

         // If now is not within the range of available times . . .
         if (!((pq->StartTimeOfDay < utcNowAsMinutesAfterMidnight) && (utcNowAsMinutesAfterMidnight < pq->UntilTimeOfDay)))
         {
            statusReport = statusReport + " Is not available at this time of day. ";
         }
      }
   };

See Also

Reference

StartTimeOfDay

UntilTimeOfDay

DateTime

PrintQueueStatus

& Operator (C# Reference)

FlagsAttribute

GetPrintQueues

PrintServer

LocalPrintServer

EnumeratedPrintQueueTypes

PrintQueue

Concepts

Documents in WPF

Printing Overview