打印队列并非总是每天 24 小时可用。 它们具有可设置的开始时间和结束时间属性,以便在一天中的特定时间使其不可用。 例如,此功能可用于在下午 5 点之后保留打印机,以独占使用某个部门。 该部门将使用与其他部门不同的队列来维护打印机。 其他部门的队列将在下午5点后不可用,而优先部门的队列则可以随时使用。
此外,打印作业本身可以设置为仅在指定的时间范围内可打印。
Microsoft .NET Framework API 中公开的 PrintQueue 和 PrintSystemJobInfo 类提供了远程检查给定打印作业当前是否可以在给定队列上打印的方法。
示例:
下面的示例是可以诊断打印作业问题的示例。
此类函数有两个主要步骤,如下所示。
- 读取 StartTimeOfDay 的 UntilTimeOfDay 和 PrintQueue 属性,以确定当前时间是否介于两者之间。 
- 读取 StartTimeOfDay 的 UntilTimeOfDay 和 PrintSystemJobInfo 属性,以确定当前时间是否介于两者之间。 
但复杂之处在于这些属性不是 DateTime 对象。 相反,它们是 Int32 对象,这些对象将一天中的时间表示为自午夜以来的分钟数。 此外,这不是当前时区的子夜,而是协调世界时(UTC)的子夜。
第一个代码示例演示了静态方法 ReportQueueAndJobAvailability,它传递了一个 并调用帮助程序方法来确定作业是否可以在当前时间打印,如果不能,则确定何时可以打印PrintSystemJobInfo。 请注意,没有将 PrintQueue 传递给该方法。 这是因为 PrintSystemJobInfo 在其 HostingPrintQueue 属性中包含对队列的引用。
从属方法包括重载的 ReportAvailabilityAtThisTime 方法,该方法可以采用 或 PrintQueue 作为参数PrintSystemJobInfo。 还有一个 TimeConverter.ConvertToLocalHumanReadableTime。 下面讨论了所有这些方法。
ReportQueueAndJobAvailability 方法首先检查队列或打印作业此时是否不可用。 如果其中任一项不可用,它将检查队列是否不可用。 如果不可用,方法将报告这种情况,并说明队列何时会再次可用。 然后它会检查作业,如果作业不可用,它会报告下一个可以打印的时间跨度。 最后,该方法报告作业可以打印的最早时间。 即后面两项时间中较晚的一项。
- 打印队列下次可用的时间。 
- 打印作业下次可用的时间。 
报告一天中的时间时,还会调用 ToShortTimeString 方法,因为此方法会禁止显示输出中的年、月和日。 不能将打印队列或打印作业的可用性限制为特定的年份、月或日。
static void ReportQueueAndJobAvailability (PrintSystemJobInfo^ theJob) 
{
   if (!(ReportAvailabilityAtThisTime(theJob->HostingPrintQueue) && ReportAvailabilityAtThisTime(theJob)))
   {
      if (!ReportAvailabilityAtThisTime(theJob->HostingPrintQueue))
      {
         Console::WriteLine("\nThat queue is not available at this time of day." + "\nJobs in the queue will start printing again at {0}", TimeConverter::ConvertToLocalHumanReadableTime(theJob->HostingPrintQueue->StartTimeOfDay).ToShortTimeString());
         // TimeConverter class is defined in the complete sample
      }
      if (!ReportAvailabilityAtThisTime(theJob))
      {
         Console::WriteLine("\nThat job is set to print only between {0} and {1}", TimeConverter::ConvertToLocalHumanReadableTime(theJob->StartTimeOfDay).ToShortTimeString(), TimeConverter::ConvertToLocalHumanReadableTime(theJob->UntilTimeOfDay).ToShortTimeString());
      }
      Console::WriteLine("\nThe job will begin printing as soon as it reaches the top of the queue after:");
      if (theJob->StartTimeOfDay > theJob->HostingPrintQueue->StartTimeOfDay)
      {
         Console::WriteLine(TimeConverter::ConvertToLocalHumanReadableTime(theJob->StartTimeOfDay).ToShortTimeString());
      } else
      {
         Console::WriteLine(TimeConverter::ConvertToLocalHumanReadableTime(theJob->HostingPrintQueue->StartTimeOfDay).ToShortTimeString());
      }
   }
};
internal static void ReportQueueAndJobAvailability(PrintSystemJobInfo theJob)
{
    if (!(ReportAvailabilityAtThisTime(theJob.HostingPrintQueue) && ReportAvailabilityAtThisTime(theJob)))
    {
        if (!ReportAvailabilityAtThisTime(theJob.HostingPrintQueue))
        {
            Console.WriteLine("\nThat queue is not available at this time of day." +
                "\nJobs in the queue will start printing again at {0}",
                 TimeConverter.ConvertToLocalHumanReadableTime(theJob.HostingPrintQueue.StartTimeOfDay).ToShortTimeString());
            // TimeConverter class is defined in the complete sample
        }
        if (!ReportAvailabilityAtThisTime(theJob))
        {
            Console.WriteLine("\nThat job is set to print only between {0} and {1}",
                TimeConverter.ConvertToLocalHumanReadableTime(theJob.StartTimeOfDay).ToShortTimeString(),
                TimeConverter.ConvertToLocalHumanReadableTime(theJob.UntilTimeOfDay).ToShortTimeString());
        }
        Console.WriteLine("\nThe job will begin printing as soon as it reaches the top of the queue after:");
        if (theJob.StartTimeOfDay > theJob.HostingPrintQueue.StartTimeOfDay)
        {
            Console.WriteLine(TimeConverter.ConvertToLocalHumanReadableTime(theJob.StartTimeOfDay).ToShortTimeString());
        }
        else
        {
            Console.WriteLine(TimeConverter.ConvertToLocalHumanReadableTime(theJob.HostingPrintQueue.StartTimeOfDay).ToShortTimeString());
        }
    }//end if at least one is not available
}//end ReportQueueAndJobAvailability
Friend Shared Sub ReportQueueAndJobAvailability(ByVal theJob As PrintSystemJobInfo)
    If Not(ReportAvailabilityAtThisTime(theJob.HostingPrintQueue) AndAlso ReportAvailabilityAtThisTime(theJob)) Then
        If Not ReportAvailabilityAtThisTime(theJob.HostingPrintQueue) Then
            Console.WriteLine(vbLf & "That queue is not available at this time of day." & vbLf & "Jobs in the queue will start printing again at {0}", TimeConverter.ConvertToLocalHumanReadableTime(theJob.HostingPrintQueue.StartTimeOfDay).ToShortTimeString())
            ' TimeConverter class is defined in the complete sample
        End If
        If Not ReportAvailabilityAtThisTime(theJob) Then
            Console.WriteLine(vbLf & "That job is set to print only between {0} and {1}", TimeConverter.ConvertToLocalHumanReadableTime(theJob.StartTimeOfDay).ToShortTimeString(), TimeConverter.ConvertToLocalHumanReadableTime(theJob.UntilTimeOfDay).ToShortTimeString())
        End If
        Console.WriteLine(vbLf & "The job will begin printing as soon as it reaches the top of the queue after:")
        If theJob.StartTimeOfDay > theJob.HostingPrintQueue.StartTimeOfDay Then
            Console.WriteLine(TimeConverter.ConvertToLocalHumanReadableTime(theJob.StartTimeOfDay).ToShortTimeString())
        Else
            Console.WriteLine(TimeConverter.ConvertToLocalHumanReadableTime(theJob.HostingPrintQueue.StartTimeOfDay).ToShortTimeString())
        End If
    End If 'end if at least one is not available
End Sub
ReportAvailabilityAtThisTime 方法的两个重载除了传递给它们的类型外是相同的,因此下面仅介绍 版本PrintQueue。
注释
除了类型不同之外,其方法相同,这一事实引发了一个问题:为什么示例没有创建泛型方法 ReportAvailabilityAtThisTime<T>。 原因是,此类方法必须限制为具有 StartTimeOfDay 和 UntilTimeOfDay 方法调用的属性的类,但泛型方法只能限制为单个类,继承树中唯一 PrintQueue 和 PrintSystemJobInfo 的类是 PrintSystemObject 没有此类属性。
ReportAvailabilityAtThisTime 方法(在下面的代码示例中显示)首先将  sentinel 变量初始化为 Booleantrue。 如果队列不可用,它将被重置为 false。
接下来,该方法检查开始时间和“直到”时间是否相同。 如果是,则队列始终可用,因此该方法返回 true。
如果队列并非始终可用,则该方法使用静态 UtcNow 属性以 DateTime 对象的形式获取当前时间。 (我们不需要本地时间,因为 StartTimeOfDay 和 UntilTimeOfDay 属性本身都是 UTC 时间。
但是,这两个属性不是 DateTime 对象。 它们是 Int32,将时间表示为 UTC 午夜后的分钟数。 因此,我们必须将 DateTime 对象转换为午夜之后的分钟数。 完成后,该方法只检查“现在”是否在队列的开始时间和“截至”时间之间,如果“现在”不在这两个时间之间,则将标记设置为 false,并返回 sentinel。
static Boolean ReportAvailabilityAtThisTime (PrintQueue^ pq) 
{
   Boolean available = true;
   if (pq->StartTimeOfDay != pq->UntilTimeOfDay)
   {
      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)))
      {
         available = false;
      }
   }
   return available;
};
private static Boolean ReportAvailabilityAtThisTime(PrintQueue pq)
{
    Boolean available = true;
    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)))
        {
            available = false;
        }
    }
    return available;
}//end ReportAvailabilityAtThisTime
Private Shared Function ReportAvailabilityAtThisTime(ByVal pq As PrintQueue) As Boolean
    Dim available As Boolean = True
    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
            available = False
        End If
    End If
    Return available
End Function 'end ReportAvailabilityAtThisTime
TimeConverter.ConvertToLocalHumanReadableTime 方法(在下面代码示例中提供)不使用Microsoft .NET Framework 引入的任何方法,因此讨论是简短的。 该方法具有双重转换任务:它必须采用一个整数,表示午夜后几分钟,并将其转换为人工可读时间,并且必须将其转换为本地时间。 它首先创建设置为午夜 UTC 的 DateTime 对象,然后使用 AddMinutes 方法添加传递给该方法的分钟数来实现此目的。 这会返回一个新的 DateTime 表示传递给该方法的原始时间。 然后,ToLocalTime 方法将此转换为本地时间。
private ref class TimeConverter {
internal: 
   static DateTime ConvertToLocalHumanReadableTime (Int32 timeInMinutesAfterUTCMidnight) 
   {
      // Construct a UTC midnight object.
      // Must start with current date so that the local Daylight Savings system, if any, will be taken into account.
      DateTime utcNow = DateTime::UtcNow;
      DateTime utcMidnight = DateTime(utcNow.Year, utcNow.Month, utcNow.Day, 0, 0, 0, DateTimeKind::Utc);
      // Add the minutes passed into the method in order to get the intended UTC time.
      Double minutesAfterUTCMidnight = ((Double)timeInMinutesAfterUTCMidnight);
      DateTime utcTime = utcMidnight.AddMinutes(minutesAfterUTCMidnight);
      // Convert to local time.
      DateTime localTime = utcTime.ToLocalTime();
      return localTime;
   };
};
class TimeConverter
{
    // Convert time as minutes past UTC midnight into human readable time in local time zone.
    internal static DateTime ConvertToLocalHumanReadableTime(Int32 timeInMinutesAfterUTCMidnight)
    {
        // Construct a UTC midnight object.
        // Must start with current date so that the local Daylight Savings system, if any, will be taken into account.
        DateTime utcNow = DateTime.UtcNow;
        DateTime utcMidnight = new DateTime(utcNow.Year, utcNow.Month, utcNow.Day, 0, 0, 0, DateTimeKind.Utc);
        // Add the minutes passed into the method in order to get the intended UTC time.
        Double minutesAfterUTCMidnight = (Double)timeInMinutesAfterUTCMidnight;
        DateTime utcTime = utcMidnight.AddMinutes(minutesAfterUTCMidnight);
        // Convert to local time.
        DateTime localTime = utcTime.ToLocalTime();
        return localTime;
    }// end ConvertToLocalHumanReadableTime
}//end TimeConverter class
Friend Class TimeConverter
    ' Convert time as minutes past UTC midnight into human readable time in local time zone.
    Friend Shared Function ConvertToLocalHumanReadableTime(ByVal timeInMinutesAfterUTCMidnight As Int32) As Date
        ' Construct a UTC midnight object.
        ' Must start with current date so that the local Daylight Savings system, if any, will be taken into account.
        Dim utcNow As Date = Date.UtcNow
        Dim utcMidnight As New Date(utcNow.Year, utcNow.Month, utcNow.Day, 0, 0, 0, DateTimeKind.Utc)
        ' Add the minutes passed into the method in order to get the intended UTC time.
        Dim minutesAfterUTCMidnight As Double = CType(timeInMinutesAfterUTCMidnight, Double)
        Dim utcTime As Date = utcMidnight.AddMinutes(minutesAfterUTCMidnight)
        ' Convert to local time.
        Dim localTime As Date = utcTime.ToLocalTime()
        Return localTime
    End Function ' end ConvertToLocalHumanReadableTime
End Class