.NET/C#中如何高效获取PrintDocument的打印任务ID?
高效获取PrintDocument打印任务ID的方案
首先明确:PrintDocument的BeginPrint事件无法直接获取打印任务ID——这个事件触发时,打印任务还未提交到打印队列,系统尚未分配Job ID。下面是两种更高效的实现方式:
方法一:用PrintQueue.AddJob替代PrintDocument.Print()
这种方式能直接拿到任务ID,完全不需要遍历队列,步骤清晰:
- 获取目标打印机的PrintQueue实例
- 调用
AddJob()创建打印任务,直接得到包含JobIdentifier的PrintSystemJobInfo对象 - 将PrintDocument的输出定向到该任务的打印流
- 启动打印并获取任务ID用于后续跟踪
代码示例:
using System.Drawing.Printing; using System.Printing; public int PrintAndGetJobId(PrintDocument pd, string printerName) { using (var printServer = new LocalPrintServer()) { // 获取目标打印机队列 var printQueue = printServer.GetPrintQueue(printerName); // 创建打印任务,直接拿到任务信息 using (var printJob = printQueue.AddJob(pd.DocumentName)) { // 将PrintDocument的输出写入任务流 using (var jobStream = printJob.JobStream) { pd.PrintController = new StandardPrintController(); pd.PrintToStream(jobStream); } // 返回任务ID return printJob.JobIdentifier; } } }
注意:需确保打印机队列有足够权限,且PrintDocument的纸张大小、分辨率等设置与打印机队列匹配,避免打印异常。
方法二:直接调用Win32打印池API(StartDocPrinter)
如果需要更底层的控制,可以通过P/Invoke调用Win32打印池API,在提交任务时同步获取Job ID。核心是用StartDocPrinter函数启动任务,该函数的返回值就是任务ID。
代码示例:
using System; using System.Drawing.Printing; using System.Runtime.InteropServices; public class PrintJobTracker { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct DOC_INFO_1 { public string pDocName; public string pOutputFile; public string pDataType; } [DllImport("winspool.drv", CharSet = CharSet.Unicode, SetLastError = true)] private static extern int OpenPrinter(string pPrinterName, out IntPtr hPrinter, IntPtr pDefault); [DllImport("winspool.drv", CharSet = CharSet.Unicode, SetLastError = true)] private static extern int StartDocPrinter(IntPtr hPrinter, int level, ref DOC_INFO_1 pDocInfo); [DllImport("winspool.drv", SetLastError = true)] private static extern int EndDocPrinter(IntPtr hPrinter); [DllImport("winspool.drv", SetLastError = true)] private static extern int ClosePrinter(IntPtr hPrinter); public int PrintWithDirectJobId(PrintDocument pd, string printerName) { IntPtr hPrinter = IntPtr.Zero; int jobId = 0; try { // 打开打印机 if (OpenPrinter(printerName, out hPrinter, IntPtr.Zero) == 0) throw new System.ComponentModel.Win32Exception(); // 准备文档信息 var docInfo = new DOC_INFO_1 { pDocName = pd.DocumentName, pDataType = "RAW" }; // 启动打印任务,获取Job ID jobId = StartDocPrinter(hPrinter, 1, ref docInfo); if (jobId == 0) throw new System.ComponentModel.Win32Exception(); // 关联PrintDocument到当前打印机句柄并启动打印 pd.PrinterSettings.PrinterName = printerName; pd.Print(); } finally { // 释放打印机资源 if (hPrinter != IntPtr.Zero) { EndDocPrinter(hPrinter); ClosePrinter(hPrinter); } } return jobId; } }
注意:必须妥善处理打印机句柄的释放,避免资源泄漏;同时要保证PrintDocument的打印流程与Win32 API调用同步。
后续进度查询
拿到Job ID后,无需遍历队列,直接通过PrintQueue.GetJob(jobId)获取PrintSystemJobInfo对象,实时查询JobStatus、NumberOfPagesPrinted等进度信息:
public PrintSystemJobInfo GetPrintJobStatus(int jobId, string printerName) { using (var printServer = new LocalPrintServer()) { var printQueue = printServer.GetPrintQueue(printerName); return printQueue.GetJob(jobId); } }
内容的提问来源于stack exchange,提问作者Alejandro Mendez
相关产品推荐
相关产品推荐

