feat: 初始功能
This commit is contained in:
9
Services/Printing/IPrintJobExecutor.cs
Normal file
9
Services/Printing/IPrintJobExecutor.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using CloudPrint.Client.Models;
|
||||
|
||||
namespace CloudPrint.Client.Services.Printing;
|
||||
|
||||
public interface IPrintJobExecutor
|
||||
{
|
||||
bool CanHandle(PrintJob job);
|
||||
Task ExecuteAsync(PrintJob job, IProgress<int>? progress, CancellationToken cancellationToken = default);
|
||||
}
|
||||
6
Services/Printing/IPrinterStatusReader.cs
Normal file
6
Services/Printing/IPrinterStatusReader.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace CloudPrint.Client.Services.Printing;
|
||||
|
||||
public interface IPrinterStatusReader
|
||||
{
|
||||
PrinterStatusSnapshot Read(string printerName);
|
||||
}
|
||||
7
Services/Printing/IWindowsPrintQueueService.cs
Normal file
7
Services/Printing/IWindowsPrintQueueService.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace CloudPrint.Client.Services.Printing;
|
||||
|
||||
public interface IWindowsPrintQueueService
|
||||
{
|
||||
WindowsPrintQueueJob? FindLatestJob(string printerName, string documentName);
|
||||
bool CancelJob(string printerName, int jobId);
|
||||
}
|
||||
88
Services/Printing/ImagePrintJobExecutor.cs
Normal file
88
Services/Printing/ImagePrintJobExecutor.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using CloudPrint.Client.Models;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Printing;
|
||||
using System.IO;
|
||||
|
||||
namespace CloudPrint.Client.Services.Printing;
|
||||
|
||||
public sealed class ImagePrintJobExecutor : IPrintJobExecutor
|
||||
{
|
||||
private readonly IWindowsPrintQueueService _queueService;
|
||||
|
||||
private static readonly HashSet<string> SupportedExtensions = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
".jpg", ".jpeg", ".png", ".bmp", ".gif"
|
||||
};
|
||||
|
||||
public ImagePrintJobExecutor(IWindowsPrintQueueService queueService)
|
||||
{
|
||||
_queueService = queueService;
|
||||
}
|
||||
|
||||
public bool CanHandle(PrintJob job)
|
||||
{
|
||||
return SupportedExtensions.Contains(Path.GetExtension(job.FilePath));
|
||||
}
|
||||
|
||||
public Task ExecuteAsync(PrintJob job, IProgress<int>? progress, CancellationToken cancellationToken = default)
|
||||
{
|
||||
PrintFileGuards.EnsurePrintableFile(job);
|
||||
|
||||
return Task.Run(() =>
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
using var image = Image.FromFile(job.FilePath);
|
||||
using var document = new PrintDocument
|
||||
{
|
||||
DocumentName = string.IsNullOrWhiteSpace(job.FileName) ? job.JobNo : job.FileName,
|
||||
PrinterSettings =
|
||||
{
|
||||
PrinterName = job.PrinterName,
|
||||
Copies = (short)Math.Clamp(job.Copies, 1, short.MaxValue)
|
||||
}
|
||||
};
|
||||
|
||||
if (document.PrinterSettings.CanDuplex)
|
||||
{
|
||||
document.PrinterSettings.Duplex = job.Duplex ? Duplex.Vertical : Duplex.Simplex;
|
||||
}
|
||||
|
||||
document.DefaultPageSettings.Color = job.Color;
|
||||
document.PrintPage += (_, args) => DrawImageToPage(image, args);
|
||||
progress?.Report(20);
|
||||
document.Print();
|
||||
CaptureWindowsJob(job, document.DocumentName);
|
||||
progress?.Report(100);
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
private void CaptureWindowsJob(PrintJob job, string documentName)
|
||||
{
|
||||
var queueJob = _queueService.FindLatestJob(job.PrinterName, documentName);
|
||||
if (queueJob is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
job.WindowsJobId = queueJob.JobId;
|
||||
job.WindowsJobStatus = queueJob.Status;
|
||||
}
|
||||
|
||||
private static void DrawImageToPage(Image image, PrintPageEventArgs args)
|
||||
{
|
||||
if (args.Graphics is null)
|
||||
{
|
||||
throw new InvalidOperationException("打印图形上下文不可用");
|
||||
}
|
||||
|
||||
var bounds = args.MarginBounds;
|
||||
var ratio = Math.Min(bounds.Width / (double)image.Width, bounds.Height / (double)image.Height);
|
||||
var width = (int)(image.Width * ratio);
|
||||
var height = (int)(image.Height * ratio);
|
||||
var x = bounds.Left + (bounds.Width - width) / 2;
|
||||
var y = bounds.Top + (bounds.Height - height) / 2;
|
||||
|
||||
args.Graphics.DrawImage(image, x, y, width, height);
|
||||
args.HasMorePages = false;
|
||||
}
|
||||
}
|
||||
100
Services/Printing/PdfPrintJobExecutor.cs
Normal file
100
Services/Printing/PdfPrintJobExecutor.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using CloudPrint.Client.Models;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace CloudPrint.Client.Services.Printing;
|
||||
|
||||
public sealed class PdfPrintJobExecutor : IPrintJobExecutor
|
||||
{
|
||||
private readonly IWindowsPrintQueueService _queueService;
|
||||
|
||||
public PdfPrintJobExecutor(IWindowsPrintQueueService queueService)
|
||||
{
|
||||
_queueService = queueService;
|
||||
}
|
||||
|
||||
public bool CanHandle(PrintJob job)
|
||||
{
|
||||
return string.Equals(Path.GetExtension(job.FilePath), ".pdf", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public async Task ExecuteAsync(PrintJob job, IProgress<int>? progress, CancellationToken cancellationToken = default)
|
||||
{
|
||||
PrintFileGuards.EnsurePrintableFile(job);
|
||||
progress?.Report(10);
|
||||
|
||||
var sumatraPath = FindSumatraPdfExecutable();
|
||||
if (!string.IsNullOrWhiteSpace(sumatraPath))
|
||||
{
|
||||
await PrintWithSumatraAsync(sumatraPath, job, cancellationToken).ConfigureAwait(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
await PrintWithShellAsync(job, cancellationToken).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
CaptureWindowsJob(job);
|
||||
|
||||
progress?.Report(100);
|
||||
}
|
||||
|
||||
private void CaptureWindowsJob(PrintJob job)
|
||||
{
|
||||
var documentName = Path.GetFileName(job.FilePath);
|
||||
var queueJob = _queueService.FindLatestJob(job.PrinterName, documentName);
|
||||
if (queueJob is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
job.WindowsJobId = queueJob.JobId;
|
||||
job.WindowsJobStatus = queueJob.Status;
|
||||
}
|
||||
|
||||
private static async Task PrintWithSumatraAsync(string sumatraPath, PrintJob job, CancellationToken cancellationToken)
|
||||
{
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = sumatraPath,
|
||||
Arguments = $"-print-to \"{job.PrinterName}\" -silent -exit-when-done \"{job.FilePath}\"",
|
||||
CreateNoWindow = true,
|
||||
UseShellExecute = false
|
||||
};
|
||||
|
||||
using var process = Process.Start(startInfo) ?? throw new InvalidOperationException("启动 SumatraPDF 失败");
|
||||
await process.WaitForExitAsync(cancellationToken).ConfigureAwait(true);
|
||||
if (process.ExitCode != 0)
|
||||
{
|
||||
throw new InvalidOperationException($"SumatraPDF 打印失败,退出码:{process.ExitCode}");
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task PrintWithShellAsync(PrintJob job, CancellationToken cancellationToken)
|
||||
{
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = job.FilePath,
|
||||
Verb = "printto",
|
||||
Arguments = $"\"{job.PrinterName}\"",
|
||||
UseShellExecute = true,
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
};
|
||||
|
||||
using var process = Process.Start(startInfo)
|
||||
?? throw new InvalidOperationException("无法调用系统默认 PDF 打印程序,请安装 SumatraPDF 或配置 PDF 默认打印程序");
|
||||
await process.WaitForExitAsync(cancellationToken).ConfigureAwait(true);
|
||||
}
|
||||
|
||||
private static string? FindSumatraPdfExecutable()
|
||||
{
|
||||
var candidates = new[]
|
||||
{
|
||||
Environment.GetEnvironmentVariable("SUMATRA_PDF_PATH"),
|
||||
@"C:\Program Files\SumatraPDF\SumatraPDF.exe",
|
||||
@"C:\Program Files (x86)\SumatraPDF\SumatraPDF.exe"
|
||||
};
|
||||
|
||||
return candidates.FirstOrDefault(path => !string.IsNullOrWhiteSpace(path) && File.Exists(path));
|
||||
}
|
||||
}
|
||||
22
Services/Printing/PrintFileGuards.cs
Normal file
22
Services/Printing/PrintFileGuards.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using CloudPrint.Client.Models;
|
||||
using System.IO;
|
||||
|
||||
namespace CloudPrint.Client.Services.Printing;
|
||||
|
||||
internal static class PrintFileGuards
|
||||
{
|
||||
public static void EnsurePrintableFile(PrintJob job)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(job);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(job.PrinterName))
|
||||
{
|
||||
throw new InvalidOperationException("打印机名称不能为空");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(job.FilePath) || !File.Exists(job.FilePath))
|
||||
{
|
||||
throw new FileNotFoundException("待打印文件不存在", job.FilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
10
Services/Printing/PrinterStatusSnapshot.cs
Normal file
10
Services/Printing/PrinterStatusSnapshot.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using CloudPrint.Client.Models;
|
||||
|
||||
namespace CloudPrint.Client.Services.Printing;
|
||||
|
||||
public sealed class PrinterStatusSnapshot
|
||||
{
|
||||
public PrinterStatus Status { get; init; } = PrinterStatus.Unknown;
|
||||
public int QueueCount { get; init; }
|
||||
public string Message { get; init; } = string.Empty;
|
||||
}
|
||||
94
Services/Printing/RawPrintJobExecutor.cs
Normal file
94
Services/Printing/RawPrintJobExecutor.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
using CloudPrint.Client.Helpers;
|
||||
using CloudPrint.Client.Models;
|
||||
using System.IO;
|
||||
|
||||
namespace CloudPrint.Client.Services.Printing;
|
||||
|
||||
public sealed class RawPrintJobExecutor : IPrintJobExecutor
|
||||
{
|
||||
public bool CanHandle(PrintJob job) => true;
|
||||
|
||||
public async Task ExecuteAsync(PrintJob job, IProgress<int>? progress, CancellationToken cancellationToken = default)
|
||||
{
|
||||
PrintFileGuards.EnsurePrintableFile(job);
|
||||
|
||||
if (!WindowsPrintApi.OpenPrinter(job.PrinterName, out var printerHandle, IntPtr.Zero))
|
||||
{
|
||||
throw new InvalidOperationException($"打开打印机失败:{job.PrinterName}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var docInfo = new WindowsPrintApi.DocInfo
|
||||
{
|
||||
DocumentName = string.IsNullOrWhiteSpace(job.FileName) ? job.JobNo : job.FileName,
|
||||
DataType = "RAW"
|
||||
};
|
||||
|
||||
var windowsJobId = WindowsPrintApi.StartDocPrinter(printerHandle, 1, docInfo);
|
||||
if (windowsJobId == 0)
|
||||
{
|
||||
throw new InvalidOperationException("启动打印文档失败");
|
||||
}
|
||||
|
||||
job.WindowsJobId = windowsJobId;
|
||||
job.WindowsJobStatus = "已提交到 Windows 打印队列";
|
||||
|
||||
try
|
||||
{
|
||||
if (!WindowsPrintApi.StartPagePrinter(printerHandle))
|
||||
{
|
||||
throw new InvalidOperationException("启动打印页面失败");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await WriteFileToPrinterAsync(printerHandle, job.FilePath, progress, cancellationToken).ConfigureAwait(true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
WindowsPrintApi.EndPagePrinter(printerHandle);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
WindowsPrintApi.EndDocPrinter(printerHandle);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
WindowsPrintApi.ClosePrinter(printerHandle);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task WriteFileToPrinterAsync(IntPtr printerHandle, string filePath, IProgress<int>? progress, CancellationToken cancellationToken)
|
||||
{
|
||||
const int bufferSize = 64 * 1024;
|
||||
var buffer = new byte[bufferSize];
|
||||
var fileInfo = new FileInfo(filePath);
|
||||
long totalWritten = 0;
|
||||
|
||||
await using var stream = File.OpenRead(filePath);
|
||||
while (true)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var read = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length), cancellationToken).ConfigureAwait(true);
|
||||
if (read == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var chunk = read == buffer.Length ? buffer : buffer[..read];
|
||||
if (!WindowsPrintApi.WritePrinter(printerHandle, chunk, read, out var written) || written != read)
|
||||
{
|
||||
throw new InvalidOperationException("写入打印机失败");
|
||||
}
|
||||
|
||||
totalWritten += written;
|
||||
var percent = fileInfo.Length == 0 ? 100 : (int)Math.Min(99, totalWritten * 100 / fileInfo.Length);
|
||||
progress?.Report(percent);
|
||||
}
|
||||
|
||||
progress?.Report(100);
|
||||
}
|
||||
}
|
||||
12
Services/Printing/WindowsPrintQueueJob.cs
Normal file
12
Services/Printing/WindowsPrintQueueJob.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace CloudPrint.Client.Services.Printing;
|
||||
|
||||
public sealed class WindowsPrintQueueJob
|
||||
{
|
||||
public int JobId { get; init; }
|
||||
public string PrinterName { get; init; } = string.Empty;
|
||||
public string DocumentName { get; init; } = string.Empty;
|
||||
public string Status { get; init; } = string.Empty;
|
||||
public int Position { get; init; }
|
||||
public int TotalPages { get; init; }
|
||||
public int PagesPrinted { get; init; }
|
||||
}
|
||||
164
Services/Printing/WindowsPrintQueueService.cs
Normal file
164
Services/Printing/WindowsPrintQueueService.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
using System.Management;
|
||||
using System.Runtime.InteropServices;
|
||||
using CloudPrint.Client.Helpers;
|
||||
|
||||
namespace CloudPrint.Client.Services.Printing;
|
||||
|
||||
public sealed class WindowsPrintQueueService : IWindowsPrintQueueService
|
||||
{
|
||||
public WindowsPrintQueueJob? FindLatestJob(string printerName, string documentName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(printerName) || string.IsNullOrWhiteSpace(documentName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return EnumerateJobs(printerName)
|
||||
.Where(job => job.DocumentName.Contains(documentName, StringComparison.OrdinalIgnoreCase)
|
||||
|| documentName.Contains(job.DocumentName, StringComparison.OrdinalIgnoreCase))
|
||||
.OrderByDescending(job => job.JobId)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CancelJob(string printerName, int jobId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(printerName) || jobId <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CancelJobWithWinSpool(printerName, jobId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var job in QueryJobObjects(printerName))
|
||||
{
|
||||
var currentJobId = Convert.ToInt32(job["JobId"] ?? 0);
|
||||
if (currentJobId != jobId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
job.InvokeMethod("Delete", null);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static IEnumerable<WindowsPrintQueueJob> EnumerateJobs(string printerName)
|
||||
{
|
||||
var spoolerJobs = EnumerateJobsWithWinSpool(printerName);
|
||||
if (spoolerJobs.Count > 0)
|
||||
{
|
||||
return spoolerJobs;
|
||||
}
|
||||
|
||||
return EnumerateJobsWithWmi(printerName);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<WindowsPrintQueueJob> EnumerateJobsWithWinSpool(string printerName)
|
||||
{
|
||||
if (!WindowsPrintApi.OpenPrinter(printerName, out var printerHandle, IntPtr.Zero))
|
||||
{
|
||||
return Array.Empty<WindowsPrintQueueJob>();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
const int maxJobs = 256;
|
||||
_ = WindowsPrintApi.EnumJobs(printerHandle, 0, maxJobs, 1, IntPtr.Zero, 0, out var bytesNeeded, out _);
|
||||
if (bytesNeeded <= 0)
|
||||
{
|
||||
return Array.Empty<WindowsPrintQueueJob>();
|
||||
}
|
||||
|
||||
var buffer = Marshal.AllocHGlobal(bytesNeeded);
|
||||
try
|
||||
{
|
||||
if (!WindowsPrintApi.EnumJobs(printerHandle, 0, maxJobs, 1, buffer, bytesNeeded, out _, out var jobsReturned))
|
||||
{
|
||||
return Array.Empty<WindowsPrintQueueJob>();
|
||||
}
|
||||
|
||||
var jobs = new List<WindowsPrintQueueJob>(jobsReturned);
|
||||
var itemSize = Marshal.SizeOf<WindowsPrintApi.JobInfo1>();
|
||||
for (var index = 0; index < jobsReturned; index++)
|
||||
{
|
||||
var itemPointer = IntPtr.Add(buffer, index * itemSize);
|
||||
var jobInfo = Marshal.PtrToStructure<WindowsPrintApi.JobInfo1>(itemPointer);
|
||||
jobs.Add(new WindowsPrintQueueJob
|
||||
{
|
||||
JobId = jobInfo.JobId,
|
||||
PrinterName = printerName,
|
||||
DocumentName = jobInfo.DocumentName ?? string.Empty,
|
||||
Status = string.IsNullOrWhiteSpace(jobInfo.StatusText) ? $"0x{jobInfo.Status:X}" : jobInfo.StatusText,
|
||||
Position = jobInfo.Position,
|
||||
TotalPages = jobInfo.TotalPages,
|
||||
PagesPrinted = jobInfo.PagesPrinted
|
||||
});
|
||||
}
|
||||
|
||||
return jobs;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(buffer);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_ = WindowsPrintApi.ClosePrinter(printerHandle);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool CancelJobWithWinSpool(string printerName, int jobId)
|
||||
{
|
||||
if (!WindowsPrintApi.OpenPrinter(printerName, out var printerHandle, IntPtr.Zero))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return WindowsPrintApi.SetJob(printerHandle, jobId, 0, IntPtr.Zero, WindowsPrintApi.JobControlDelete)
|
||||
|| WindowsPrintApi.SetJob(printerHandle, jobId, 0, IntPtr.Zero, WindowsPrintApi.JobControlCancel);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_ = WindowsPrintApi.ClosePrinter(printerHandle);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<WindowsPrintQueueJob> EnumerateJobsWithWmi(string printerName)
|
||||
{
|
||||
foreach (var job in QueryJobObjects(printerName))
|
||||
{
|
||||
yield return new WindowsPrintQueueJob
|
||||
{
|
||||
JobId = Convert.ToInt32(job["JobId"] ?? 0),
|
||||
PrinterName = printerName,
|
||||
DocumentName = Convert.ToString(job["Document"]) ?? string.Empty,
|
||||
Status = Convert.ToString(job["JobStatus"]) ?? string.Empty
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<ManagementObject> QueryJobObjects(string printerName)
|
||||
{
|
||||
var escapedName = printerName.Replace("\\", "\\\\", StringComparison.Ordinal).Replace("'", "''", StringComparison.Ordinal);
|
||||
using var searcher = new ManagementObjectSearcher($"SELECT JobId, Document, JobStatus, Name FROM Win32_PrintJob WHERE Name LIKE '{escapedName},%'");
|
||||
foreach (ManagementObject job in searcher.Get())
|
||||
{
|
||||
yield return job;
|
||||
}
|
||||
}
|
||||
}
|
||||
119
Services/Printing/WindowsPrinterStatusReader.cs
Normal file
119
Services/Printing/WindowsPrinterStatusReader.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using CloudPrint.Client.Models;
|
||||
using System.Management;
|
||||
|
||||
namespace CloudPrint.Client.Services.Printing;
|
||||
|
||||
public sealed class WindowsPrinterStatusReader : IPrinterStatusReader
|
||||
{
|
||||
public PrinterStatusSnapshot Read(string printerName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(printerName))
|
||||
{
|
||||
return new PrinterStatusSnapshot { Status = PrinterStatus.Unknown, Message = "打印机名称为空" };
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return ReadCore(printerName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new PrinterStatusSnapshot
|
||||
{
|
||||
Status = PrinterStatus.Unknown,
|
||||
Message = $"状态读取失败:{ex.Message}"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static PrinterStatusSnapshot ReadCore(string printerName)
|
||||
{
|
||||
using var searcher = new ManagementObjectSearcher(
|
||||
"SELECT Name, WorkOffline, PrinterStatus, DetectedErrorState FROM Win32_Printer");
|
||||
|
||||
foreach (ManagementObject printer in searcher.Get())
|
||||
{
|
||||
var name = Convert.ToString(printer["Name"]);
|
||||
if (!string.Equals(name, printerName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var workOffline = Convert.ToBoolean(printer["WorkOffline"] ?? false);
|
||||
var printerStatus = Convert.ToUInt32(printer["PrinterStatus"] ?? 2u);
|
||||
var errorState = Convert.ToUInt32(printer["DetectedErrorState"] ?? 0u);
|
||||
var queueCount = CountQueueJobs(printerName);
|
||||
|
||||
var status = MapStatus(workOffline, printerStatus, errorState, queueCount);
|
||||
return new PrinterStatusSnapshot
|
||||
{
|
||||
Status = status,
|
||||
QueueCount = queueCount,
|
||||
Message = BuildMessage(workOffline, printerStatus, errorState)
|
||||
};
|
||||
}
|
||||
|
||||
return new PrinterStatusSnapshot
|
||||
{
|
||||
Status = PrinterStatus.Offline,
|
||||
Message = "未找到打印机"
|
||||
};
|
||||
}
|
||||
|
||||
private static int CountQueueJobs(string printerName)
|
||||
{
|
||||
var escapedName = printerName.Replace("\\", "\\\\", StringComparison.Ordinal).Replace("'", "''", StringComparison.Ordinal);
|
||||
using var searcher = new ManagementObjectSearcher($"SELECT Name FROM Win32_PrintJob WHERE Name LIKE '{escapedName},%'");
|
||||
return searcher.Get().Count;
|
||||
}
|
||||
|
||||
private static PrinterStatus MapStatus(bool workOffline, uint printerStatus, uint errorState, int queueCount)
|
||||
{
|
||||
if (workOffline || printerStatus == 7)
|
||||
{
|
||||
return PrinterStatus.Offline;
|
||||
}
|
||||
|
||||
if (errorState is >= 3 and <= 11 || printerStatus == 6)
|
||||
{
|
||||
return PrinterStatus.Error;
|
||||
}
|
||||
|
||||
if (printerStatus == 4 || queueCount > 0)
|
||||
{
|
||||
return PrinterStatus.Printing;
|
||||
}
|
||||
|
||||
return printerStatus == 3 ? PrinterStatus.Idle : PrinterStatus.Unknown;
|
||||
}
|
||||
|
||||
private static string BuildMessage(bool workOffline, uint printerStatus, uint errorState)
|
||||
{
|
||||
if (workOffline)
|
||||
{
|
||||
return "脱机";
|
||||
}
|
||||
|
||||
return errorState switch
|
||||
{
|
||||
3 => "缺纸",
|
||||
4 => "卡纸",
|
||||
5 => "碳粉不足",
|
||||
6 => "门打开",
|
||||
7 => "需要人工干预",
|
||||
8 => "离线",
|
||||
9 => "维修中",
|
||||
10 => "输出纸盘已满",
|
||||
11 => "缺纸",
|
||||
_ => printerStatus switch
|
||||
{
|
||||
3 => "就绪",
|
||||
4 => "打印中",
|
||||
5 => "预热中",
|
||||
6 => "已停止",
|
||||
7 => "离线",
|
||||
_ => string.Empty
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user