You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#控制台程序在Windows任务计划中打印PDF的开源方案咨询

适用于Windows任务计划的C# PDF打印解决方案

问题根源

你遇到的核心问题是System.Drawing.Common和基于它的PDF打印库(比如你使用的PdfiumViewer)依赖GDI+图形环境,而Windows任务计划在无桌面会话的后台运行时,无法提供完整的图形上下文,导致程序挂起、无响应甚至无限循环。

推荐的开源解决方案

1. 调用Windows打印池API(无GDI+依赖)

直接调用Win32打印池底层接口,绕开图形环境依赖,将PDF文件以RAW格式发送至打印机驱动:

using System;
using System.Runtime.InteropServices;
using System.IO;

public class RawPdfPrinter
{
    [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool OpenPrinter(string pPrinterName, out IntPtr hPrinter, IntPtr pDefault);

    [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool ClosePrinter(IntPtr hPrinter);

    [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool StartDocPrinter(IntPtr hPrinter, int Level, [In] ref DOCINFOA di);

    [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool EndDocPrinter(IntPtr hPrinter);

    [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool StartPagePrinter(IntPtr hPrinter);

    [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool EndPagePrinter(IntPtr hPrinter);

    [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBuf, int cbBuf, out int pcWritten);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    private struct DOCINFOA
    {
        [MarshalAs(UnmanagedType.LPStr)] public string pDocName;
        [MarshalAs(UnmanagedType.LPStr)] public string pOutputFile;
        [MarshalAs(UnmanagedType.LPStr)] public string pDataType;
    }

    public bool PrintPdf(string printerName, string pdfPath)
    {
        IntPtr printerHandle = IntPtr.Zero;
        try
        {
            if (!OpenPrinter(printerName, out printerHandle, IntPtr.Zero))
                throw new System.ComponentModel.Win32Exception();

            byte[] pdfBytes = File.ReadAllBytes(pdfPath);

            DOCINFOA docInfo = new DOCINFOA
            {
                pDocName = "Auto PDF Print Job",
                pDataType = "RAW"
            };

            if (!StartDocPrinter(printerHandle, 1, ref docInfo))
                throw new System.ComponentModel.Win32Exception();

            if (!StartPagePrinter(printerHandle))
                throw new System.ComponentModel.Win32Exception();

            IntPtr pdfPtr = Marshal.AllocHGlobal(pdfBytes.Length);
            Marshal.Copy(pdfBytes, 0, pdfPtr, pdfBytes.Length);
            if (!WritePrinter(printerHandle, pdfPtr, pdfBytes.Length, out _))
                throw new System.ComponentModel.Win32Exception();
            Marshal.FreeHGlobal(pdfPtr);

            EndPagePrinter(printerHandle);
            EndDocPrinter(printerHandle);

            return true;
        }
        catch (Exception ex)
        {
            SentrySdk.CaptureException(ex);
            return false;
        }
        finally
        {
            if (printerHandle != IntPtr.Zero)
                ClosePrinter(printerHandle);
        }
    }
}

2. 使用Ghostscript.NET(命令行级底层处理)

Ghostscript是老牌开源PDF处理工具,其.NET封装Ghostscript.NET可在无图形界面环境下直接调用,通过命令行模式完成打印:

using Ghostscript.NET;
using Ghostscript.NET.Processor;

public bool PrintPdfWithGhostscript(string printerName, string pdfPath)
{
    try
    {
        using (var processor = new GhostscriptProcessor())
        {
            var settings = new GhostscriptSettings(GhostscriptVersion.Latest);
            settings.Device = GhostscriptDeviceName.mswinpr2;
            settings.OutputFile = $"\\\\spool\\{printerName}";
            
            processor.StartProcessing(
                new string[] { "-dBATCH", "-dNOPAUSE", "-dNOSAFER", $"-sDEVICE={settings.Device}", $"-sOutputFile={settings.OutputFile}", pdfPath },
                null
            );
        }
        return true;
    }
    catch (Exception ex)
    {
        SentrySdk.CaptureException(ex);
        return false;
    }
}

任务计划配置关键注意事项

  • 运行程序的用户账户必须拥有目标打印机的访问权限,建议使用本地管理员或域账户(避免SYSTEM账户,可能存在权限限制)。
  • 任务计划中勾选**“不管用户是否登录都要运行”**,按需勾选“使用最高权限运行”。
  • 程序放置目录需授予读写权限,避免放在C:\Program Files这类受系统保护的路径。

内容的提问来源于stack exchange,提问作者lucario

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 19:15:59