SDK风格项目无法设置打印机纸张尺寸但Framework项目正常
PrintDocument.DefaultPageSettings.PaperSize设置不生效 问题背景
使用System.Drawing.Printing命名空间,通过PrintDocument.DefaultPageSettings.PaperSize属性设置文档纸张尺寸时,SDK风格的.NET Framework 4.7.2项目中该设置始终未实际生效,但传统Framework风格项目(同样目标框架net472、引用相同System.Drawing程序集、打印逻辑完全一致)却能正常工作。
观察到的具体现象
- 打印输出始终与打印机驱动默认尺寸一致(控制面板中打印机偏好设置的自定义尺寸:宽300、高200,对应驱动DEVMODE的
dmFormName、dmPaperLength、dmPaperWidth); - 驱动日志显示DevMode输入来自全局默认设置,而非代码中指定的配置;
- SDK项目中设置
PaperSize后,PrintDocument.DefaultPageSettings.PrintableArea属性未被驱动更新,仍保持300x200;而Framework项目中该属性会更新为代码设置的200x100。
根本原因
这是SDK风格.NET Framework项目与传统Framework项目在打印设置初始化逻辑上的差异导致的:
传统Framework项目的初始化行为
传统Framework项目在创建PrintDocument实例时,会直接从打印机驱动获取当前的默认DevMode,随后允许代码通过DefaultPageSettings修改该DevMode,修改后的设置会正确传递给打印机驱动。SDK风格.NET Framework项目的初始化行为
SDK风格项目(即使目标是.NET Framework)在初始化PrintDocument时,会优先读取**应用程序配置文件(app.config)**中可能存在的打印设置;若配置中无指定内容,会尝试复用全局默认DevMode缓存,而非每次从驱动获取最新设置并允许代码覆盖。这就导致代码中设置的PaperSize无法正确覆盖全局默认值,驱动依然使用控制面板中的偏好设置。
更具体地说,SDK风格项目的System.Drawing.Printing相关组件初始化时,会通过System.Configuration读取配置,而传统Framework项目则直接依赖驱动的即时设置。当代码尝试修改PaperSize时,SDK项目中的DevMode实例可能处于“只读”状态(或未正确标记为已修改),导致驱动忽略代码中的设置,继续使用全局默认值。
解决方案
可以通过以下方式强制SDK风格项目正确应用自定义纸张尺寸:
显式创建并修改DevMode:跳过
DefaultPageSettings.PaperSize的直接设置,手动获取打印机的DevMode,修改其纸张尺寸参数后再赋值给PrintDocument:using (var printDoc = new PrintDocument()) { // 获取打印机的默认DevMode var devMode = printDoc.PrinterSettings.GetHdevmode(printDoc.DefaultPageSettings); try { // 转换为可编辑的DevMode结构体 var editableDevMode = (DEVMODE)Marshal.PtrToStructure(devMode, typeof(DEVMODE)); // 设置自定义纸张尺寸(示例:宽200,高100,单位为百分之一英寸) editableDevMode.dmPaperWidth = 200; editableDevMode.dmPaperLength = 100; editableDevMode.dmFields |= DM_PAPERSIZE | DM_PAPERWIDTH | DM_PAPERLENGTH; // 将修改后的DevMode写回 Marshal.StructureToPtr(editableDevMode, devMode, false); printDoc.PrinterSettings.SetHdevmode(devMode); } finally { // 释放DevMode内存 Marshal.FreeHGlobal(devMode); } // 执行打印 printDoc.Print(); }注:需要定义
DEVMODE结构体和对应的常量(DM_PAPERSIZE等),可通过P/Invoke声明获取。清除配置中的默认打印设置:检查项目的
app.config文件,若存在<system.drawing.printing>相关配置,删除或注释掉这些配置,强制项目从驱动获取即时设置。强制刷新页面设置:在设置
PaperSize后,调用printDoc.DefaultPageSettings.Margins = printDoc.DefaultPageSettings.Margins;(或其他无意义的修改),标记页面设置为已修改,促使驱动重新读取设置。
内容的提问来源于stack exchange,提问作者etberg

