Solution for a memory consumption problem when printing many PDFs (VB.NET)

Q: I've built a process that iterates through all the pdf files in a
folder and sends them to the default printer. Each time it prints a
file, it takes some more memory and never releases it. Because each job
contains 100+ files, the memory used quickly exceeds that of the client
machine and ultimately crashes the application, etc...

I've isolated the line where the memory jump happens.
I've tried several different ways of destroying objects, but none
seem to get the memory back.

The code is a follows:

Imports System.IO

Imports System
Imports System.Drawing
Imports System.Drawing.Printing

Imports pdftron
Imports pdftron.PDF
Imports pdftron.Common
Imports pdftron.Filters

Public Class frmPrint

    Private pdfdoc As PDFDoc = Nothing
    Private pdfdraw As PDFDraw = Nothing
    Private pageitr As PageIterator = Nothing

    Private Sub btnBrowse_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles btnBrowse.Click
        txtFolder.Text = FolderBrowserDialog1.SelectedPath
    End Sub

    Private Sub btnPrint_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles btnPrint.Click
        Dim myFiles() As FileInfo
        Dim oPDFTargetFolder As DirectoryInfo
        Dim myTimer As Date

        If Trim(txtFolder.Text) = "" Then
            MessageBox.Show("Folder not specified.", "Invalid Folder",
MessageBoxButtons.OK, MessageBoxIcon.Warning)
            oPDFTargetFolder = New DirectoryInfo(txtFolder.Text)
            If Not oPDFTargetFolder.Exists Then
                MessageBox.Show("Folder does not exist.", "Invalid
Folder", MessageBoxButtons.OK, MessageBoxIcon.Warning)
                myFiles = oPDFTargetFolder.GetFiles("*.PDF")
                If myFiles.GetLength(0) <= 0 Then
                    MessageBox.Show("No PDF files to print.", "No
Files", MessageBoxButtons.OK, MessageBoxIcon.Information)
                    Me.Cursor = Cursors.WaitCursor
                    Array.Sort(myFiles, New CompareFileInfo)

                    For Each oFileInfo As FileInfo In myFiles

                        PDFPrint(txtFolder.Text, oFileInfo.Name)

                        'Just a timer to keep the printer queue under
                        myTimer = Now
                              Do While
(Now.Subtract(myTimer).TotalSeconds < 6)

                    'MessageBox.Show("Printing Complete.", "Printing
Complete", MessageBoxButtons.OK)
                    Me.Cursor = Cursors.Default
                End If
            End If
        End If

    End Sub

    Private Sub PDFPrint(ByVal strFolder As String, ByVal strFile As
        Dim pdPrinter As PrintDocument = New PrintDocument

            Console.WriteLine("Opening the input file...")
            pdfdoc = New PDFDoc(strFolder & "\" & strFile)
            pageitr = pdfdoc.PageBegin

            pdfdraw = New PDFDraw

            AddHandler pdPrinter.PrintPage, AddressOf PrintPage
        Catch e As PDFNetException
        End Try
    End Sub

    Private Sub PrintPage(ByVal sender As Object, ByVal ev As
        Dim gr As Graphics = ev.Graphics
        gr.PageUnit = GraphicsUnit.Inch
        Dim rectPage As Rectangle = ev.PageBounds

        Dim dpi As Single = gr.DpiX
        If dpi > 300 Then dpi = 300

        Dim left As Double = (rectPage.Left -
ev.PageSettings.HardMarginX) / 100
        Dim right As Double = (rectPage.Right -
ev.PageSettings.HardMarginX) / 100
        Dim top As Double = (rectPage.Top -
ev.PageSettings.HardMarginY) / 100
        Dim bottom As Double = (rectPage.Bottom -
ev.PageSettings.HardMarginY) / 100
        Dim rect As PDFTRON.PDF.Rect = New Rect(left * 72, bottom * 72,
right * 72, top * 72)


            'This line causes the memory usage that never gets
            pdfdraw.DrawInRect(pageitr.Current, gr, rect)

        Catch ex As Exception
            Console.WriteLine("Printing Error: " + ex.ToString)
        End Try


        ev.HasMorePages = Not (pageitr = pdfdoc.PageEnd)
    End Sub

End Class

Since you are repeatedly re-creating PDFDraw objects you should call
pdfdraw.Close() just before pdfdoc.Close() in your PDFPrint method.
Otherwise pdfdraw objects may keep on accumulating because .NET garbage
collector is sometimes not very smart.

Another option is to allocate PDFDraw only one time and to keep on
reusing it to print/render all PDF documents.


I can't use your suggestion to use pdfdraw.Close() because there isn't
any 'Close' function for PDFDraw.

I took your suggestion to only use the PDFDraw object once.

Instead of using...
pdfdraw = New PDFDraw
I've changed it to read...
If pdfdraw Is Nothing Then
     pdfdraw = New PDFDraw
End If

This works, but still holds the last page in memory and won't let it
go until the application quits.)

You are right, there is no Close() method is PDFDraw. The method you
should use is pdfdraw.Dispose(). Calling dispose between print calls
to different documents will free all allocated memory.