0

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
Craig
  • 2,248
  • 1
  • 19
  • 23
AndyDF
  • 59
  • 5

1 Answers1

1

For anyone stumbling upon this issue, it seems that the problem was solved with update KB5001649

Make sure you and/or your customers keep Windows up to date and this issue won't affect your projects.

AndyDF
  • 59
  • 5