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

C# VSTO触发打印前将默认黑白打印机切换为彩色打印的实现问题

问题根源

原有代码配置不生效的核心原因有两点:

  • 自行实例化的PrinterSettings、PageSettings是.NET框架独立维护的打印配置对象,和Word进程持有的打印上下文、系统级打印机配置没有绑定关系,修改这些对象属性不会同步到Word的打印流程,调用PrintOut时Word仍会读取打印机默认的黑白配置执行输出。
  • 代码中PrinterSettings.SupportsColor调用写法错误,SupportsColor是实例属性,直接通过类名调用无法获取当前选中打印机的实际彩色支持能力。
实现方案

Word的PrintOut方法本身没有提供直接传入彩色/黑白输出模式的参数,需要通过系统打印API修改当前打印机的DEVMODE配置(打印任务的核心参数结构),再触发Word打印,流程如下:

  1. 记录当前打印机原有彩色配置,用于打印完成后还原,避免影响用户后续其他打印任务
  2. 通过Win32打印API打开当前打印机,读取其DEVMODE结构
  3. 修改DEVMODE结构中的彩色输出标记位,标记字段修改有效
  4. 将修改后的配置写回打印上下文,执行Word打印逻辑
  5. 在finally块中还原打印机原有配置,即使打印过程抛错也不会污染用户默认设置

首先需要定义Win32 API互操作所需的结构与方法:

using System;
using System.Drawing.Printing;
using System.Runtime.InteropServices;
using Word = Microsoft.Office.Interop.Word;
using System.Globalization;

// DEVMODE结构定义,仅保留需要操作的字段即可
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct DEVMODE
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string dmDeviceName;
    public short dmSpecVersion;
    public short dmDriverVersion;
    public short dmSize;
    public short dmDriverExtra;
    public int dmFields;
    public int dmPositionX;
    public int dmPositionY;
    public int dmDisplayOrientation;
    public int dmDisplayFixedOutput;
    public short dmColor;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct PRINTER_DEFAULTS
{
    public IntPtr pDatatype;
    public IntPtr pDevMode;
    public int DesiredAccess;
}

public class PrintHelper
{
    [DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool OpenPrinter(string pPrinterName, out IntPtr phPrinter, ref PRINTER_DEFAULTS 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 int DocumentProperties(IntPtr hWnd, IntPtr hPrinter, string pDeviceName, IntPtr pDevModeOutput, IntPtr pDevModeInput, int fMode);

    private const int DM_IN_BUFFER = 8;
    private const int DM_OUT_BUFFER = 2;
    private const int DM_COLOR = 0x800;
    private const int DMCOLOR_COLOR = 2;
    private const int DMCOLOR_MONOCHROME = 1;
    private const int PRINTER_ALL_ACCESS = 0x800000 | 0x1;

核心打印逻辑实现:

public static void PrintDocumentInColor(Word.Document doc, Word.WdPrintOutRange printRange, int firstPage = 1, int lastPage = 1, int copies = 1)
    {
        string originalPrinterName = doc.Application.ActivePrinter;
        bool originalColorSetting = false;
        IntPtr hPrinter = IntPtr.Zero;
        IntPtr pDevMode = IntPtr.Zero;
        PrinterSettings printerSettings = new PrinterSettings();

        try
        {
            // 匹配系统识别的打印机名,自动处理Word返回的打印机名带端口后缀的问题
            printerSettings.PrinterName = originalPrinterName;
            string targetPrinterName = printerSettings.PrinterName;

            if (printerSettings.SupportsColor)
            {
                // 打开打印机获取配置
                PRINTER_DEFAULTS defaults = new PRINTER_DEFAULTS();
                defaults.DesiredAccess = PRINTER_ALL_ACCESS;
                OpenPrinter(targetPrinterName, out hPrinter, ref defaults);

                // 分配DEVMODE内存并读取现有配置
                int devModeSize = DocumentProperties(IntPtr.Zero, hPrinter, targetPrinterName, IntPtr.Zero, IntPtr.Zero, 0);
                pDevMode = Marshal.AllocHGlobal(devModeSize);
                DocumentProperties(IntPtr.Zero, hPrinter, targetPrinterName, pDevMode, IntPtr.Zero, DM_OUT_BUFFER);

                DEVMODE devMode = Marshal.PtrToStructure<DEVMODE>(pDevMode);
                originalColorSetting = devMode.dmColor == DMCOLOR_COLOR;

                // 修改为彩色输出模式,标记字段有效
                devMode.dmColor = DMCOLOR_COLOR;
                devMode.dmFields |= DM_COLOR;

                // 写回修改后的配置
                Marshal.StructureToPtr(devMode, pDevMode, true);
                DocumentProperties(IntPtr.Zero, hPrinter, targetPrinterName, pDevMode, pDevMode, DM_IN_BUFFER | DM_OUT_BUFFER);
            }

            // 执行打印,参数和原有逻辑保持一致
            object background = true;
            object append = false;
            object range = printRange;
            object to = Type.Missing;
            object from = Type.Missing;
            if (printRange == Word.WdPrintOutRange.wdPrintFromTo)
            {
                to = lastPage.ToString(CultureInfo.InvariantCulture);
                from = firstPage.ToString(CultureInfo.InvariantCulture);
            }
            object copyCount = copies;
            object printToFile = false;
            object collate = true;

            doc.PrintOut(ref background, ref append, ref range, ref Type.Missing, ref from, ref to,
                                ref Type.Missing, ref copyCount, ref Type.Missing, ref Type.Missing,
                                ref printToFile, ref collate, ref Type.Missing, ref Type.Missing,
                                ref Type.Missing, ref Type.Missing, ref Type.Missing, ref Type.Missing);
        }
        finally
        {
            // 还原原有打印机配置
            if (pDevMode != IntPtr.Zero && hPrinter != IntPtr.Zero)
            {
                DEVMODE devMode = Marshal.PtrToStructure<DEVMODE>(pDevMode);
                devMode.dmColor = originalColorSetting ? DMCOLOR_COLOR : DMCOLOR_MONOCHROME;
                devMode.dmFields |= DM_COLOR;
                Marshal.StructureToPtr(devMode, pDevMode, true);
                DocumentProperties(IntPtr.Zero, hPrinter, printerSettings.PrinterName, pDevMode, pDevMode, DM_IN_BUFFER | DM_OUT_BUFFER);
                ClosePrinter(hPrinter);
                Marshal.FreeHGlobal(pDevMode);
            }
        }
    }
}
注意事项
  • 不要尝试直接修改.NET自带PrinterSettings.DefaultPageSettings.Color属性,这个修改仅对当前进程内用.NET原生PrintDocument实现的打印逻辑生效,Word作为独立COM组件走自身打印栈,完全读不到这个配置。
  • 不要混淆文档内容颜色和打印机输出颜色配置,PageSetup下的颜色相关属性控制的是文档本身的内容渲染效果,和打印机输出黑白/彩色无关,修改这类属性不会改变打印输出模式。
  • 所有打印机配置修改必须放在try-finally块中执行还原,否则会修改用户系统全局的打印机默认配置,影响后续所有打印任务。
  • 部分小众打印机驱动会使用私有DEVMODE扩展字段控制黑白/彩色输出,不识别标准dmColor标记,这类场景需要对应打印机厂商的SDK做适配,没有通用解决方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 01:54:23