164 lines
5.4 KiB
C#
164 lines
5.4 KiB
C#
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;
|
|
}
|
|
}
|
|
} |