如何:确定此时是否可以打印一项打印作业
打印队列并非始终每天 24 小时都可用。 打印队列具有开始和结束时间属性,可以设置这些属性以使队列在一天中的某些时间不可用。 例如,此功能可用于在下午 5 点之后保留一台打印机供某个部门专用。 该部门将使用与其他部门不同的队列来维护打印机。 其他部门的队列将设置为在下午 5 点之后不可用,而优先部门的队列可以设置为随时可用。
此外,打印作业本身可以设置为只能在指定的时间范围内打印。
Microsoft .NET Framework 的 API 中公开的 PrintQueue 和 PrintSystemJobInfo 类提供了一种远程检查给定打印作业当前是否可以在给定队列上打印的方法。
示例
以下是一个可以诊断打印作业问题的示例。
此类函数有两个主要步骤,如下所示。
读取 PrintQueue 的 StartTimeOfDay 和 UntilTimeOfDay 属性以确定当前时间是否在它们之间。
读取 PrintSystemJobInfo 的 StartTimeOfDay 和 UntilTimeOfDay 属性以确定当前时间是否在它们之间。
但是由于这些属性不是 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 方法(在下面的代码示例中显示)首先将 Boolean sentinel 变量初始化为 true
。 如果队列不可用,它将被重置为 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