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.

188 lines
5.9 KiB

1 month ago
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Printing;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. public class PrintMonitor
  8. {
  9. public async Task<string> CheckPrintStatusAsync(
  10. string printerName,
  11. string targetJobName,
  12. CancellationToken cancellationToken,
  13. TimeSpan? timeout = null)
  14. {
  15. var timeoutValue = timeout ?? TimeSpan.FromSeconds(60);
  16. using (var timeoutCts = new CancellationTokenSource(timeoutValue))
  17. using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(
  18. cancellationToken,
  19. timeoutCts.Token))
  20. {
  21. try
  22. {
  23. while (!linkedCts.IsCancellationRequested)
  24. {
  25. var isCompleted = await Task.Run(() =>
  26. {
  27. // 关键修改:每个操作线程创建自己的PrintServer
  28. using (var printServer = new LocalPrintServer())
  29. {
  30. var printQueue = GetPrintQueue(printServer, printerName);
  31. return CheckJobs(printQueue, targetJobName);
  32. }
  33. }, linkedCts.Token);
  34. if (isCompleted) return "Success";
  35. await Task.Delay(1000, linkedCts.Token);
  36. }
  37. }
  38. catch (OperationCanceledException ex)
  39. {
  40. HandleCancellation(timeoutCts, cancellationToken, timeoutValue);
  41. throw;
  42. }
  43. }
  44. //throw new OperationCanceledException();
  45. return "Success";
  46. }
  47. private PrintQueue GetPrintQueue(LocalPrintServer server, string printerName)
  48. {
  49. try
  50. {
  51. return string.IsNullOrEmpty(printerName)
  52. ? server.DefaultPrintQueue
  53. : server.GetPrintQueue(printerName);
  54. }
  55. catch (Exception ex)
  56. {
  57. throw new InvalidOperationException($"获取打印机队列失败: {ex.Message}");
  58. }
  59. }
  60. private bool CheckJobs(PrintQueue queue, string targetJobName)
  61. {
  62. queue.Refresh();
  63. var jobs = queue.GetPrintJobInfoCollection()
  64. ?? throw new Exception("无法获取打印任务列表");
  65. foreach (PrintSystemJobInfo job in jobs)
  66. {
  67. if (job.Name.Contains(targetJobName))
  68. {
  69. try
  70. {
  71. CheckJobStatus(job);
  72. return job.JobStatus.HasFlag(PrintJobStatus.Completed);
  73. }
  74. catch (Exception ex)
  75. {
  76. // 仅传播需要处理的异常
  77. if (!IsExpectedStatus(job.JobStatus))
  78. throw new Exception("关键打印错误", ex);
  79. }
  80. }
  81. }
  82. return false;
  83. }
  84. private bool IsExpectedStatus(PrintJobStatus status)
  85. {
  86. // 定义可忽略的临时状态
  87. var ignorableStatuses = new[]
  88. {
  89. PrintJobStatus.Printing,
  90. PrintJobStatus.Spooling,
  91. PrintJobStatus.Retained
  92. };
  93. return ignorableStatuses.Any(s => status.HasFlag(s));
  94. }
  95. private void CheckJobStatus(PrintSystemJobInfo job)
  96. {
  97. // 成功状态直接返回
  98. if (job.JobStatus.HasFlag(PrintJobStatus.Completed))
  99. return;
  100. // 状态分类配置
  101. var ignorableStatuses = new[]
  102. {
  103. PrintJobStatus.Printing, // 正在打印
  104. PrintJobStatus.Retained, // 任务保留
  105. PrintJobStatus.Spooling, // 后台处理
  106. PrintJobStatus.Paused // 用户主动暂停
  107. };
  108. var criticalStatuses = new[]
  109. {
  110. PrintJobStatus.Error, // 通用错误
  111. PrintJobStatus.PaperOut, // 缺纸
  112. //PrintJobStatus.PaperJam, // 卡纸
  113. PrintJobStatus.Offline // 脱机
  114. };
  115. // 第一步:检查可忽略的中间状态
  116. if (ignorableStatuses.Any(s => job.JobStatus.HasFlag(s)))
  117. {
  118. return; // 静默继续等待
  119. }
  120. // 第二步:检测关键错误状态
  121. var criticalStatus = criticalStatuses.FirstOrDefault(s =>
  122. job.JobStatus.HasFlag(s));
  123. if (criticalStatus != default)
  124. {
  125. throw new Exception($"打印错误: {GetStatusDescription(criticalStatus)}");
  126. }
  127. // 第三步:处理未知状态
  128. throw new Exception($"无法处理的打印状态: {FormatCompositeStatus(job.JobStatus)}");
  129. }
  130. // 辅助方法:获取状态描述
  131. private string GetStatusDescription(PrintJobStatus status)
  132. {
  133. // 优先处理复合状态
  134. if ((status & (PrintJobStatus.Error | PrintJobStatus.Offline)) != 0)
  135. {
  136. return "设备故障";
  137. }
  138. // 处理单一状态
  139. switch (status)
  140. {
  141. case PrintJobStatus.PaperOut:
  142. return "缺纸";
  143. case PrintJobStatus.Spooling:
  144. return "后台处理中";
  145. default:
  146. return status.ToString();
  147. }
  148. }
  149. // 辅助方法:格式化复合状态
  150. private string FormatCompositeStatus(PrintJobStatus status)
  151. {
  152. var activeFlags = Enum.GetValues(typeof(PrintJobStatus))
  153. .Cast<PrintJobStatus>()
  154. .Where(s => s != PrintJobStatus.None && status.HasFlag(s))
  155. .Select(s => s.ToString());
  156. return string.Join(" | ", activeFlags);
  157. }
  158. private void HandleCancellation(
  159. CancellationTokenSource timeoutCts,
  160. CancellationToken originalToken,
  161. TimeSpan timeout)
  162. {
  163. if (timeoutCts.IsCancellationRequested)
  164. throw new TimeoutException($"操作超时 ({timeout.TotalMinutes}分钟)");
  165. if (originalToken.IsCancellationRequested)
  166. throw new OperationCanceledException("用户取消操作");
  167. }
  168. }