You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
189 lines
5.9 KiB
189 lines
5.9 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Printing;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
public class PrintMonitor
|
|
{
|
|
public async Task<string> CheckPrintStatusAsync(
|
|
string printerName,
|
|
string targetJobName,
|
|
CancellationToken cancellationToken,
|
|
TimeSpan? timeout = null)
|
|
{
|
|
var timeoutValue = timeout ?? TimeSpan.FromSeconds(60);
|
|
|
|
using (var timeoutCts = new CancellationTokenSource(timeoutValue))
|
|
using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(
|
|
cancellationToken,
|
|
timeoutCts.Token))
|
|
{
|
|
try
|
|
{
|
|
while (!linkedCts.IsCancellationRequested)
|
|
{
|
|
var isCompleted = await Task.Run(() =>
|
|
{
|
|
// 关键修改:每个操作线程创建自己的PrintServer
|
|
using (var printServer = new LocalPrintServer())
|
|
{
|
|
var printQueue = GetPrintQueue(printServer, printerName);
|
|
return CheckJobs(printQueue, targetJobName);
|
|
}
|
|
}, linkedCts.Token);
|
|
|
|
if (isCompleted) return "Success";
|
|
await Task.Delay(1000, linkedCts.Token);
|
|
}
|
|
}
|
|
catch (OperationCanceledException ex)
|
|
{
|
|
HandleCancellation(timeoutCts, cancellationToken, timeoutValue);
|
|
throw;
|
|
}
|
|
}
|
|
//throw new OperationCanceledException();
|
|
return "Success";
|
|
}
|
|
|
|
private PrintQueue GetPrintQueue(LocalPrintServer server, string printerName)
|
|
{
|
|
try
|
|
{
|
|
return string.IsNullOrEmpty(printerName)
|
|
? server.DefaultPrintQueue
|
|
: server.GetPrintQueue(printerName);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new InvalidOperationException($"获取打印机队列失败: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private bool CheckJobs(PrintQueue queue, string targetJobName)
|
|
{
|
|
queue.Refresh();
|
|
var jobs = queue.GetPrintJobInfoCollection()
|
|
?? throw new Exception("无法获取打印任务列表");
|
|
|
|
foreach (PrintSystemJobInfo job in jobs)
|
|
{
|
|
if (job.Name.Contains(targetJobName))
|
|
{
|
|
try
|
|
{
|
|
CheckJobStatus(job);
|
|
return job.JobStatus.HasFlag(PrintJobStatus.Completed);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// 仅传播需要处理的异常
|
|
if (!IsExpectedStatus(job.JobStatus))
|
|
throw new Exception("关键打印错误", ex);
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool IsExpectedStatus(PrintJobStatus status)
|
|
{
|
|
// 定义可忽略的临时状态
|
|
var ignorableStatuses = new[]
|
|
{
|
|
PrintJobStatus.Printing,
|
|
PrintJobStatus.Spooling,
|
|
PrintJobStatus.Retained
|
|
};
|
|
|
|
return ignorableStatuses.Any(s => status.HasFlag(s));
|
|
}
|
|
private void CheckJobStatus(PrintSystemJobInfo job)
|
|
{
|
|
// 成功状态直接返回
|
|
if (job.JobStatus.HasFlag(PrintJobStatus.Completed))
|
|
return;
|
|
|
|
// 状态分类配置
|
|
var ignorableStatuses = new[]
|
|
{
|
|
PrintJobStatus.Printing, // 正在打印
|
|
PrintJobStatus.Retained, // 任务保留
|
|
PrintJobStatus.Spooling, // 后台处理
|
|
PrintJobStatus.Paused // 用户主动暂停
|
|
};
|
|
|
|
var criticalStatuses = new[]
|
|
{
|
|
PrintJobStatus.Error, // 通用错误
|
|
PrintJobStatus.PaperOut, // 缺纸
|
|
//PrintJobStatus.PaperJam, // 卡纸
|
|
PrintJobStatus.Offline // 脱机
|
|
};
|
|
|
|
// 第一步:检查可忽略的中间状态
|
|
if (ignorableStatuses.Any(s => job.JobStatus.HasFlag(s)))
|
|
{
|
|
return; // 静默继续等待
|
|
}
|
|
|
|
// 第二步:检测关键错误状态
|
|
var criticalStatus = criticalStatuses.FirstOrDefault(s =>
|
|
job.JobStatus.HasFlag(s));
|
|
|
|
if (criticalStatus != default)
|
|
{
|
|
throw new Exception($"打印错误: {GetStatusDescription(criticalStatus)}");
|
|
}
|
|
|
|
// 第三步:处理未知状态
|
|
throw new Exception($"无法处理的打印状态: {FormatCompositeStatus(job.JobStatus)}");
|
|
}
|
|
|
|
// 辅助方法:获取状态描述
|
|
private string GetStatusDescription(PrintJobStatus status)
|
|
{
|
|
// 优先处理复合状态
|
|
if ((status & (PrintJobStatus.Error | PrintJobStatus.Offline)) != 0)
|
|
{
|
|
return "设备故障";
|
|
}
|
|
|
|
// 处理单一状态
|
|
switch (status)
|
|
{
|
|
case PrintJobStatus.PaperOut:
|
|
return "缺纸";
|
|
case PrintJobStatus.Spooling:
|
|
return "后台处理中";
|
|
default:
|
|
return status.ToString();
|
|
}
|
|
}
|
|
|
|
// 辅助方法:格式化复合状态
|
|
private string FormatCompositeStatus(PrintJobStatus status)
|
|
{
|
|
var activeFlags = Enum.GetValues(typeof(PrintJobStatus))
|
|
.Cast<PrintJobStatus>()
|
|
.Where(s => s != PrintJobStatus.None && status.HasFlag(s))
|
|
.Select(s => s.ToString());
|
|
|
|
return string.Join(" | ", activeFlags);
|
|
}
|
|
|
|
|
|
private void HandleCancellation(
|
|
CancellationTokenSource timeoutCts,
|
|
CancellationToken originalToken,
|
|
TimeSpan timeout)
|
|
{
|
|
if (timeoutCts.IsCancellationRequested)
|
|
throw new TimeoutException($"操作超时 ({timeout.TotalMinutes}分钟)");
|
|
|
|
if (originalToken.IsCancellationRequested)
|
|
throw new OperationCanceledException("用户取消操作");
|
|
}
|
|
}
|