feat: 初始功能
This commit is contained in:
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user