开发类Windows内置打印作业监视器:如何获取打印作业使用的端口?
获取Windows打印作业端口信息的可行方案
方法1:通过WMI关联打印机与打印作业
Win32_PrintJob的PortName字段在部分场景下可能为空或未正确填充,此时可以通过打印作业关联的打印机对象(Win32_Printer)获取端口信息:
using System.Management; var printJobsQuery = new ObjectQuery("SELECT * FROM Win32_PrintJob"); using (var searcher = new ManagementObjectSearcher(printJobsQuery)) { foreach (ManagementObject job in searcher.Get()) { // 获取关联的打印机名称 string printerName = job["PrinterName"].ToString(); // 查询对应打印机的端口信息(处理单引号转义避免语法错误) var printerQuery = new ObjectQuery($"SELECT * FROM Win32_Printer WHERE Name = '{printerName.Replace("'", "''")}'"); using (var printerSearcher = new ManagementObjectSearcher(printerQuery)) { foreach (ManagementObject printer in printerSearcher.Get()) { string portName = printer["PortName"].ToString(); Console.WriteLine($"作业ID: {job["JobId"]}, 使用端口: {portName}"); } } } }
方法2:调用Windows打印后台处理服务原生API(P/Invoke)
如果WMI仍无法获取,可直接调用Win32 API的GetJob函数,获取包含端口信息的JOB_INFO_2结构体:
using System; using System.Runtime.InteropServices; using System.Text; public class PrintJobHelper { [DllImport("winspool.drv", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool OpenPrinter(string pPrinterName, out IntPtr phPrinter, IntPtr pDefault); [DllImport("winspool.drv", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool ClosePrinter(IntPtr hPrinter); [DllImport("winspool.drv", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool GetJob(IntPtr hPrinter, int JobId, int Level, IntPtr pJob, int cbBuf, out int pcbNeeded); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct JOB_INFO_2 { public uint JobId; public string pPrinterName; public string pMachineName; public string pUserName; public string pDocument; public string pDatatype; public string pStatus; public uint Status; public uint Priority; public uint Position; public uint TotalPages; public uint PagesPrinted; public SYSTEMTIME Submitted; public SYSTEMTIME StartTime; public SYSTEMTIME UntilTime; public uint TotalBytes; public uint BytesPrinted; public string pPortName; // 端口信息字段 // 省略其他未用到的字段 } [StructLayout(LayoutKind.Sequential)] private struct SYSTEMTIME { public ushort wYear; public ushort wMonth; public ushort wDayOfWeek; public ushort wDay; public ushort wHour; public ushort wMinute; public ushort wSecond; public ushort wMilliseconds; } public static string GetPrintJobPort(int jobId, string printerName) { IntPtr hPrinter = IntPtr.Zero; try { if (!OpenPrinter(printerName, out hPrinter, IntPtr.Zero)) throw new System.ComponentModel.Win32Exception(); int bufferSize = 0; // 先获取所需缓冲区大小 GetJob(hPrinter, jobId, 2, IntPtr.Zero, 0, out bufferSize); if (bufferSize == 0) return null; IntPtr jobInfoPtr = Marshal.AllocHGlobal(bufferSize); try { if (GetJob(hPrinter, jobId, 2, jobInfoPtr, bufferSize, out _)) { JOB_INFO_2 jobInfo = Marshal.PtrToStructure<JOB_INFO_2>(jobInfoPtr); return jobInfo.pPortName; } else { throw new System.ComponentModel.Win32Exception(); } } finally { Marshal.FreeHGlobal(jobInfoPtr); } } finally { if (hPrinter != IntPtr.Zero) ClosePrinter(hPrinter); } } }
使用示例:
string port = PrintJobHelper.GetPrintJobPort(123, "MyPrinter"); Console.WriteLine($"作业端口: {port}");
方法3:检查打印后台处理程序的日志
如果以上方法都失败,可启用打印后台处理程序的日志记录:
- 打开事件查看器 → 展开应用程序和服务日志 → Microsoft → Windows → PrintService
- 启用Operational日志,打印作业的详细信息(包括端口)会被记录在事件ID为307的事件中。
内容的提问来源于stack exchange,提问作者Markus
相关产品推荐
相关产品推荐

