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? 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? 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); } }