Monday, March 7, 2011

Programmatically Printing in Adobe Reader 9 using .NET Interop

I am using VB.Net WinForms. I would like to call the Adobe Reader 9 ActiveX control to print some PDFs. I have added the ActiveX control to the VS toolbox (the dll is AcroPDF.dll, the COM name "Adobe PDF Reader". After some experiment the following code works.

Dim files As String() = Directory.GetFiles(TextBoxPath.Text, "*.pdf", SearchOption.TopDirectoryOnly)

Using ActiveXPDF As New AxAcroPDFLib.AxAcroPDF

    Me.Controls.Add(ActiveXPDF)
    ActiveXPDF.Hide()

    For Each filename As String In files

        ActiveXPDF.LoadFile(filename)
        ActiveXPDF.printAll()

        'Begin Yukky Hack    '


        Dim endTime As Date = DateAdd(DateInterval.Second, 20, Now)
        Do While Now < endTime
            My.Application.DoEvents()
        Loop

        'End Yuk   '

    Next

End Using

Without the Yuk bit this will only print some of the PDFs, it seems that the End Using statement is calling dispose on the control before it has finished printing.

Therefore it seems the call to printAll is non-blocking but I can't find a callback or status property I can query to see if the print spooling has been completed. I am missing a property/method or is there a more elegant (and more responsive) work around?

From stackoverflow
  • We ended up using Adobe's PDF Verifier for our own testing purposes. In order to do this we had to actually launch acrobat and manipulate it's interface programmatically using SendInput.

    I would be very interested in seeing if it would be possible to use an internal API instead.

  • Using this method to print multiple documents is not going to work good as you found.

    Having it work is quite tricky but here is a general description of the solution.

    I use System.Diagnostics.Process to print using myProcess.StartInfo.Verb = "Print" Then I check the Status and State of printer queue in two steps to make sure that the printing is ready enough to be able to print the next document. Use WMI and ManagementObjectSearcher to enumerate the printer info using "SELECT * FROM Win32_Printer". The logic is that I try to see if the spooling is started before continuing to print the next one.

    See http://msdn.microsoft.com/en-us/library/aa394363.aspx for the Win32_Printer WMI class.

    Christopher Edwards : +1 But that's almost as horrible hack as my 'just wait for a bit' hack! I wonder why it's such a pita. I have decided just not call call Dispose, which I suppose is even worse...
  • You can use this code to display any file with its appropriate software.

    Sub Show_Document(ByVal FILENAME As String)
        Dim p As Process = Nothing
        Try
            If My.Computer.FileSystem.FileExists(FILENAME) Then
                p = Process.Start(FILENAME)
                p.Dispose()
            End If
    
        Catch ex As Exception
    
        Finally
    
        End Try
    
    End Sub
    
    statenjason : The question is about how to print PDFs, not open them. I understand they could click "print" within the new Adobe Reader instance, but that's a usability nightmare. Also, swallowing exceptions is bad practice.

0 comments:

Post a Comment