使用 gdiplus 打印 png24 图像时 GDI 内存泄漏
Posted
技术标签:
【中文标题】使用 gdiplus 打印 png24 图像时 GDI 内存泄漏【英文标题】:GDI memory leak when printing png24 images with gdiplus 【发布时间】:2021-06-26 13:15:57 【问题描述】:在 Windows 10 下的某些打印机上打印时,我遇到了一个非常奇怪的遗留“gdi 对象”泄漏。
即使在 Microsoft 虚拟打印机(例如本示例中使用的“Microsoft XPS Document Writer”)上打印时也会发生这种情况。 当涉及 png 24 图像时,似乎会发生 GDI 泄漏。
我创建了一个测试项目来轻松重现该行为:
Imports System.Drawing
Imports System.Drawing.Printing
Module Module1
' The PNG 24 source image
Private Const SOURCE_PNG24_FILE As String = [Path to any png 24 image]
' The number of rendering repetitions
Private Const REPETITIONS As Integer = 100
Sub Main()
MsgBox("Open the task manager and check GDI objects count for this application, should be around 15.", MsgBoxStyle.Information)
' Get Microsoft XPS Document Writer printer settings
Dim prnSettings As New PrinterSettings() With .PrinterName = "Microsoft XPS Document Writer"
' Ensure printer is valid
If prnSettings.IsValid = False Then
MsgBox("Can't find Microsoft XPS Document Writer.", MsgBoxStyle.Exclamation)
Exit Sub
End If
' Init document settings
m_prtDoc = New PrintDocument With .DocumentName = "PNG24 print test",
.PrinterSettings = prnSettings
' Start printing
m_prtDoc.Print()
MsgBox("Check GDI objects count, probably thru the roof.", MsgBoxStyle.Information)
End Sub
' Document settings
Private WithEvents m_prtDoc As PrintDocument = Nothing
' Page count
Private m_iPageCount As Integer = 0
Private Sub PrintPage(sender As Object, e As PrintPageEventArgs) Handles m_prtDoc.PrintPage
Try
' Load png 24 image
Using imgSourcePng24 As Image = Image.FromFile(SOURCE_PNG24_FILE)
' Create in memory image of the same size
Using imgMemory As New Bitmap(imgSourcePng24.Width, imgSourcePng24.Height)
' Get the in-memory graphics
Using gphMemory As Graphics = Graphics.FromImage(imgMemory)
' Draw source image
gphMemory.DrawImage(imgSourcePng24, New Rectangle(Point.Empty, imgSourcePng24.Size), New Rectangle(Point.Empty, imgSourcePng24.Size), GraphicsUnit.Pixel)
End Using
' Draw in-memory image -> Comment this line and no GDI object spike
e.Graphics.DrawImage(imgMemory, New Rectangle(Point.Empty, imgMemory.Size), New Rectangle(Point.Empty, imgMemory.Size), GraphicsUnit.Pixel)
End Using
End Using
' Increase page count
m_iPageCount += 1
' Print up to REPETITIONS pages
e.HasMorePages = (m_iPageCount < REPETITIONS)
Catch ex As Exception
' Error, Stop printing
MsgBox(ex.Message, MsgBoxStyle.Critical)
End Try
End Sub
End Module
内存泄漏发生在 Windows 10 19042.867 上,而在具有相同虚拟打印机的 Windows 7 上不会发生。 png 8 或其他图像格式(例如 jpg)不会发生这种情况。
我有另一个导致相同奇怪行为的 win32 项目,似乎问题可能与 GdipCreateBitmapFromScan0 有关,它在创建新的空位图时在后台调用。
如果您将 png24 直接渲染到打印机的 Graphics 上一切正常,但如果需要中间步骤(例如裁剪)并创建空白图像,则会发生泄漏。
任何帮助表示赞赏。 谢谢。
编辑:
在运行了更多测试后,事实证明它与将 32bppARGB 图像渲染到打印机 Graphics 有关,无论其来源如何。 当您使用 argb 内容渲染内存中创建的位图时也会发生这种情况:
Private Sub PrintPage(sender As Object, e As PrintPageEventArgs) Handles m_prtDoc.PrintPage
Try
' Create in memory image
Using imgMemory As New Bitmap(600, 400)
' Fill with alpha color
Using gphMemory As Graphics = Graphics.FromImage(imgMemory)
gphMemory.Clear(Color.FromArgb(128, 255, 0, 0)) ' Red at 0.5 alpha
End Using
' Draw in-memory image
e.Graphics.DrawImageUnscaled(imgMemory, e.PageBounds.Location)
End Using
' Increase page counter
m_iPageCount += 1
' The number of rendering repetitions
Const REPETITIONS As Integer = 20
' Print up to REPETITIONS pages
e.HasMorePages = (m_iPageCount < REPETITIONS)
Catch ex As Exception
' Error, Stop printing
MsgBox(ex.Message, MsgBoxStyle.Critical)
End Try
End Sub
【问题讨论】:
一些工具可以帮助您调试泄漏:How to debug GDI Object Leaks? 感谢Xingyu Zhao,我一直在尝试其中的一些。问题是:当不是您的代码导致泄漏时,您会怎么做? 向导致泄漏的代码的所有者提交错误报告? @Craig,那就是微软 :) 旁注:PrintDocument
也实现了IDisposable
。可能还需要using
。
【参考方案1】:
对于遇到此问题的任何人,似乎问题已通过更新 KB5001649 解决
确保您和/或您的客户使 Windows 保持最新状态,此问题不会影响您的项目。
【讨论】:
以上是关于使用 gdiplus 打印 png24 图像时 GDI 内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章