C#单窗体双打印机打印Bitmap异常问题求助
我在窗体上设置了两个按钮,分别用于向PDF打印机和普通打印机打印Bitmap图像,但程序运行行为异常:
- 有时两个按钮均正常工作;
- 有时普通打印机打印空白,PDF打印正常;
- 有时普通打印机无输出,PDF打印机却打印出乱码;
- 有时PDF文件中还会出现异常artifacts。
以下是我的代码片段:
private void printBtn_Click(object sender, EventArgs e) { ... BmpToPrint = new Bitmap(tmp_bmp); tmp_bmp.Dispose(); string file = "sometext"; pdoc.PrinterSettings.PrinterName = System.Configuration.ConfigurationManager.AppSettings["Printer"]; pdoc.DocumentName = file; pdoc.DefaultPageSettings.Landscape = false; ppv.Document = pdoc; //printpreviewdialog ppv, printdocument pdoc printDialog1.Document = pdoc; if (printDialog1.PrinterSettings.IsValid) { pdoc.Print(); } } private void printPdfBtn_Click(object sender, EventArgs e) { ... BmpToPrint = new Bitmap(tmp_bmp); tmp_bmp.Dispose(); string file = "sometext" + ".pdf"; if (!Directory.Exists(directory)) { directory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); } pdoc.PrinterSettings.PrinterName = System.Configuration.ConfigurationManager.AppSettings["PdfPrinter"]; pdoc.PrinterSettings.PrintToFile = true; pdoc.DocumentName = file; pdoc.PrinterSettings.PrintFileName = Path.Combine(directory, file); pdoc.DefaultPageSettings.Landscape = false; ppv.Document = pdoc; printDialog1.Document = pdoc; if (printDialog1.PrinterSettings.IsValid) { pdoc.Print(); } } private void pdoc_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e) { e.Graphics.Clear(Color.White); Point PktWst = new Point(5, 0); e.Graphics.DrawImage(BmpToPrint, PktWst); e.HasMorePages = false; }
请问切换打印机时是否需要重置某些设置或清理相关数据?
你的问题核心在于重复使用同一个PrintDocument实例切换打印机时,没有彻底重置打印机相关的状态和资源,导致不同打印任务的设置相互干扰,加上Bitmap资源管理的潜在问题,引发了各种异常。下面是具体的修复步骤:
1. 每次打印任务使用独立的PrintDocument实例
不要复用同一个pdoc对象,因为PrinterSettings和DefaultPageSettings会保留上一次打印的状态,尤其是PrintToFile这类属性,在切换到普通打印机时如果没有重置,会导致打印行为异常。
修改思路:在每个按钮点击事件中创建新的PrintDocument实例,而不是复用全局的pdoc。示例代码:
private void printBtn_Click(object sender, EventArgs e) { // 创建新的PrintDocument实例,using自动释放资源 using (PrintDocument pdoc = new PrintDocument()) { // 用using包裹Bitmap,确保资源正确释放 using (Bitmap BmpToPrint = new Bitmap(tmp_bmp)) { string file = "sometext"; pdoc.PrinterSettings.PrinterName = System.Configuration.ConfigurationManager.AppSettings["Printer"]; // 显式关闭PrintToFile,避免PDF打印的设置残留 pdoc.PrinterSettings.PrintToFile = false; pdoc.DocumentName = file; pdoc.DefaultPageSettings.Landscape = false; // 绑定PrintPage事件,捕获当前的Bitmap实例 pdoc.PrintPage += (s, eArgs) => { eArgs.Graphics.Clear(Color.White); Point PktWst = new Point(5, 0); eArgs.Graphics.DrawImage(BmpToPrint, PktWst); eArgs.HasMorePages = false; }; printDialog1.Document = pdoc; if (printDialog1.PrinterSettings.IsValid) { pdoc.Print(); } } } } private void printPdfBtn_Click(object sender, EventArgs e) { using (PrintDocument pdoc = new PrintDocument()) { using (Bitmap BmpToPrint = new Bitmap(tmp_bmp)) { string file = "sometext" + ".pdf"; string directory = /* 你的目录变量 */; if (!Directory.Exists(directory)) { directory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); } pdoc.PrinterSettings.PrinterName = System.Configuration.ConfigurationManager.AppSettings["PdfPrinter"]; pdoc.PrinterSettings.PrintToFile = true; pdoc.PrinterSettings.PrintFileName = Path.Combine(directory, file); pdoc.DocumentName = file; pdoc.DefaultPageSettings.Landscape = false; pdoc.PrintPage += (s, eArgs) => { eArgs.Graphics.Clear(Color.White); Point PktWst = new Point(5, 0); eArgs.Graphics.DrawImage(BmpToPrint, PktWst); eArgs.HasMorePages = false; }; printDialog1.Document = pdoc; if (printDialog1.PrinterSettings.IsValid) { pdoc.Print(); } } } }
2. 修复Bitmap资源的生命周期问题
你当前的代码中tmp_bmp.Dispose()在创建BmpToPrint后立即执行,虽然new Bitmap(tmp_bmp)会复制像素数据,但如果tmp_bmp依赖外部资源(比如从文件或GDI+对象创建),提前释放可能导致BmpToPrint异常。使用using语句包裹BmpToPrint可以确保资源被正确释放,同时避免全局变量BmpToPrint被不同打印任务覆盖。
3. 若坚持复用PrintDocument,需显式重置关键设置
如果一定要复用全局的pdoc实例,必须在每次打印前彻底重置所有可能影响打印的设置:
- 重置
PrinterSettings.PrintToFile:PDF打印设为true,普通打印设为false - 清空
PrinterSettings.PrintFileName:避免残留的PDF路径干扰普通打印 - 重置
DefaultPageSettings:通过pdoc.DefaultPageSettings = new PageSettings();恢复默认值 - 重新验证
PrinterSettings.IsValid:设置打印机名称后再次确认有效性
4. 解决PDF打印的Artifacts问题
PDF打印机的Artifacts通常是GDI+绘图上下文状态未正确重置,或Bitmap分辨率与打印机不匹配导致的:
- 在
PrintPage事件中保存并恢复绘图上下文状态,避免后续操作影响当前打印:
private void pdoc_PrintPage(object sender, PrintPageEventArgs e) { // 保存当前Graphics状态,using自动恢复 using (var state = e.Graphics.Save()) { e.Graphics.Clear(Color.White); // 设置高质量插值模式,避免缩放失真 e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; Point PktWst = new Point(5, 0); e.Graphics.DrawImage(BmpToPrint, PktWst); e.HasMorePages = false; } }
- 确保Bitmap的DPI与打印机DPI匹配,或绘制时指定目标尺寸而非直接使用原始Bitmap大小。
总结
最可靠的方案是每次打印任务使用独立的PrintDocument和Bitmap实例,彻底避免不同打印任务的状态干扰,同时通过using语句确保资源被正确释放。
内容的提问来源于stack exchange,提问作者Mmaa Bbaa

