Link to home
Start Free TrialLog in
Avatar of rrouse
rrouse

asked on

Closing .PDF in Acrobat from VB.Net console app without closing the Acrobat application.

I posted a question about opening .PDF's from .Net yesterday, and it is working very well.  As a follow-up to that question, I would also like to be able to close a .PDF file opened in Acrobat from a .Net application without having to close the Acrobat window.  Is this possible?  One person mentioned something about OLE objects, but I'm not very familiar with them, so I may need some assistance if that is the answer.

Again, thanks to all for your help.  It is very much appreciated.
Avatar of Javert93
Javert93

You can use DDE to control the acrobat reader process. The available commands are list in http://partners.adobe.com/asn/acrobat/docs/iacref.pdf.
'Here is a class you can use to close any parent or child window

'The search is performed using the instr, so it looks for the occurance of the title.

'You could only specify

'_wActions.CloseParentWindow("Acrobat")
this would find and window that has acrobat in his title

'or
'_wActions.CloseChildWindow("Acrobat", "VB .NET")

'You can modify the class in order to make case insensitive.

'Hope this helps.

Private Sub FlatButton1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles FlatButton1.Click
        Dim _wActions As New WindowsActions()
        Try
            _wActions.CloseChildWindow("Adobe Acrobat", "VB .NET How to Program 2ed.pdf")
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try

        _wActions.CloseParentWindow("Adobe Acrobat")

        _wActions = Nothing
    End Sub


'This is the class, just instanciate it and use its methods
Public Class WindowsActions

    Private Delegate Function EnumChildProc(ByVal hwnd As IntPtr, ByVal lParam As Int32) As Int32

#Region "API declarations"

    Private Const WM_CLOSE As Integer = &H10

    Private Declare Function EnumChildWindows Lib "user32.dll" ( _
        ByVal hWndParent As IntPtr, ByVal lpEnumFunc As EnumChildProc, _
        ByVal lParam As Int32) As Int32

    Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" ( _
        ByVal hwnd As IntPtr, ByVal wMsg As Int32, ByVal wParam As IntPtr, _
        ByRef lParam As Object) As Int32

    Private Declare Function GetWindowTextLength Lib "user32.dll" Alias "GetWindowTextLengthA" _
        (ByVal hwnd As IntPtr) As Int32

    Private Declare Function GetWindowText Lib "user32.dll" Alias "GetWindowTextA" _
        (ByVal hwnd As IntPtr, ByVal lpString As String, ByVal cch As Int32) As Int32

#End Region

    Private _Handle As IntPtr
    Dim _Title As String

    Public Sub CloseParentWindow(ByVal Title As String)
        _Title = Title
        'Search for the window in the desktop
        EnumChildWindows(IntPtr.Zero, AddressOf FindWindow, 0)

        'If not found throw an exception
        If _Handle.Equals(IntPtr.Zero) Then
            Throw New Exception("Parent window """ & Title & """ not found.")
        Else
            'Close window
            SendMessage(_Handle, WM_CLOSE, IntPtr.Zero, 0)
        End If
    End Sub

    Public Sub CloseChildWindow(ByVal ParentTitle As String, ByVal ChildTitle As String)
        _Title = ParentTitle

        'Search for the parent window handle in the desktop
        EnumChildWindows(IntPtr.Zero, AddressOf FindWindow, 0)

        'If parent window not found throw an exception
        If _Handle.Equals(IntPtr.Zero) Then
            Throw New Exception("Parent window """ & ParentTitle & """ not found.")
        End If

        _Title = ChildTitle
        'Search for child window handle
        EnumChildWindows(_Handle, AddressOf FindWindow, 0)

        'If not found throw an exception
        If _Handle.Equals(IntPtr.Zero) Then
            Throw New Exception("Child window """ & ChildTitle & """ not found.")
        Else
            'Close child window
            SendMessage(_Handle, WM_CLOSE, IntPtr.Zero, 0)
        End If
    End Sub

    'Callback function to enum windows
    Private Function FindWindow(ByVal hwnd As IntPtr, ByVal lParam As Int32) As Int32
        Dim sSave As String

        'Get the windowtext length
        sSave = Space(GetWindowTextLength(hwnd) + 1)

        'get the window text
        GetWindowText(hwnd, sSave, Len(sSave))

        'remove the last Chr(0)
        sSave = Microsoft.VisualBasic.Left(sSave, Len(sSave) - 1)

        If Microsoft.VisualBasic.InStr(sSave, _Title) > 0 And sSave <> "" Then
            _Handle = hwnd
            Return 0 'stop enumeration
        Else
            _Handle = IntPtr.Zero
            Return 1 'continue enumeration
        End If
    End Function
End Class
Avatar of rrouse

ASKER

Hi, and thanks to both wguerram & Javert93.  

I tried implementing the solution provided by wguerram, but it isn't working.  I use "Acrobat" as the name of the parent window (which does not result in an error), but the parent window does not close.  I also believe that the child window cannot be found because the parent window is not being identified.  When using the Process.Start() command, can I tell it the name of the window?  Or, is there a standard Acrobat name used?

I also looked up the link provided by Javert93...very helpeful!  However, I don't know how to use those commands in a .Net solution.  Do you know of some examples I could look up that show how you would code them into an application?  Also, I read somewhere that DDE is not supported in .Net...is this an incorrect statement?  

Again, thanks!
If you don't get an error its means the class is finding the window.

Do you have other windows opened while doing this?

I was trying it and i realize that the title for this internet explorer page has Acrobat, so it was finding this window.

Do something:

Place a break point in the line

_Handle = hwnd ' and check the value of sSave variable

Just to see the name of the window is finding

        If Microsoft.VisualBasic.InStr(sSave, _Title) > 0 And sSave <> "" Then
            _Handle = hwnd  '<------- Break point here
            Return 0 'stop enumeration
        Else
            _Handle = IntPtr.Zero
            Return 1 'continue enumeration
        End If
Instead of the breakpoint you can also place a messagebox

       If Microsoft.VisualBasic.InStr(sSave, _Title) > 0 And sSave <> "" Then
             MsgBox(sSave)
            _Handle = hwnd  '<------- Break point here
            Return 0 'stop enumeration
        Else
            _Handle = IntPtr.Zero
            Return 1 'continue enumeration
        End If
No, DDE is supported through a set of very help Win32 API's. I need to look them up, but I will post a class later on this afternoon that will let you do exactly do everything that you want (including stuff from the last post). I just need some time to translate the API's from C++ and test the code.
ASKER CERTIFIED SOLUTION
Avatar of Javert93
Javert93

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
This code is quite helpful, but one slight correction. DdeGetLastError when used resets the error code that will be returned next time. So the following change is recommended:

                        'Throw New DdeErrorException("DdeClientTransaction", DdeGetLastError(DdeInstanceID))
                        Throw New DdeErrorException("DdeClientTransaction", errorCode)
One more comment, the wFmt parameter to the DdeClientTransaction call should be zero for type XTYP_EXECUTE, code is below.  Also I noted that I was getting a timeout error using InternalTimeout = 3000 for a 4 page PDF, so increasing the timeout seems to help.

If DdeClientTransaction(cmdBuffer, cmd.Length, ConversationHandle, IntPtr.Zero, _
                    0, XTYP_EXECUTE, InternalTimeout, result).Equals(IntPtr.Zero) Then