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.
Again, thanks to all for your help. It is very much appreciated.
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.CloseParentWind ow("Acroba t")
this would find and window that has acrobat in his title
'or
'_wActions.CloseChildWindo w("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.CloseParentWindo w("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.Ze ro, 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.Ze ro, 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.InSt r(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
'The search is performed using the instr, so it looks for the occurance of the title.
'You could only specify
'_wActions.CloseParentWind
this would find and window that has acrobat in his title
'or
'_wActions.CloseChildWindo
'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
Catch ex As Exception
MsgBox(ex.Message)
End Try
_wActions.CloseParentWindo
_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.Ze
'If not found throw an exception
If _Handle.Equals(IntPtr.Zero
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.Ze
'If parent window not found throw an exception
If _Handle.Equals(IntPtr.Zero
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
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(
'get the window text
GetWindowText(hwnd, sSave, Len(sSave))
'remove the last Chr(0)
sSave = Microsoft.VisualBasic.Left
If Microsoft.VisualBasic.InSt
_Handle = hwnd
Return 0 'stop enumeration
Else
_Handle = IntPtr.Zero
Return 1 'continue enumeration
End If
End Function
End Class
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!
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.InSt r(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
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.InSt
_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.InSt r(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
If Microsoft.VisualBasic.InSt
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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("DdeClie ntTransact ion", DdeGetLastError(DdeInstanc eID))
Throw New DdeErrorException("DdeClie ntTransact ion", errorCode)
'Throw New DdeErrorException("DdeClie
Throw New DdeErrorException("DdeClie
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(cmdBu ffer, cmd.Length, ConversationHandle, IntPtr.Zero, _
0, XTYP_EXECUTE, InternalTimeout, result).Equals(IntPtr.Zero ) Then
If DdeClientTransaction(cmdBu
0, XTYP_EXECUTE, InternalTimeout, result).Equals(IntPtr.Zero