I'm experiencing a very odd legacy "gdi object" leak when printing on certain printers under Windows 10.
It happens even when you print on Microsoft virtual printers, such as the "Microsoft XPS Document Writer" used in this example. It seems that the GDI leak takes place when png 24 images are involved.
I've created a test project to easily reproduce the behavior:
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
The memory leak happens on Windows 10 19042.867 and does not happen on Windows 7, with the same virtual printer. It does not happen with png 8 or other image formats, such as jpg.
I have another win32 project that is causing the same strange behavior and it seems that the problem might be related to the GdipCreateBitmapFromScan0, which is called behind the scenes when a new empty bitmap is created.
If you render the png24 directly onto the printer's Graphics all is fine, but if need an intermediate step (such as for cropping) and create a blank image, the leak occurs.
Any help appreciated. Thank you.
EDIT:
After running a few more tests, it turns out that it has to do with rendering 32bppARGB images to a printer Graphics, regardless of its origin. It happens also when you render an in-memory created bitmap with argb contents:
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