Solved

VBA or VBS enumerate all open Acrobat Reader documents

Posted on 2011-02-12
39
1,457 Views
Last Modified: 2012-05-11
I want to (in some type of code) find all the pdf files that are open in Acrobat Reader (I only have Reader)
Since I don't have Acrobat Pro, I can't get any API calls to work (e.g. GetObject(, "AcroExch.App"))
The solution to the related link (searching running tasks) doesn't work since it only finds the first one, and that only if the pdf was double clicked (since it parses the shell command that started the process).

I need the entire path of the files. I had the idea to try to capture it from the open windows, but don't know how well that would work since only the file name shows and it's formatted differently in XP and 7.

Any ideas?
0
Comment
Question by:TommySzalapski
  • 15
  • 11
  • 8
39 Comments
 
LVL 30

Expert Comment

by:SiddharthRout
Comment Utility
I can give you the code to display the names of all the PDF Files that are open using APIs.

I know how to get the path from the running EXE but not from File names. I am working on that at the moment.

Sid
0
 
LVL 30

Expert Comment

by:SiddharthRout
Comment Utility
There is one way I can think of...

If I close all the pdf one by one then I can get you the path of all the pdf's. I can then reopen them at the end. Would that help?

Sid
0
 
LVL 37

Author Comment

by:TommySzalapski
Comment Utility
Closing is fine. Don't bother reopening, I can do that. The version for Word and Excel closes them too (since that's the best way to iterate through Excel instances). Closing them doesn't change the command line for the process though.

Interested to see what you can up with.
0
 
LVL 37

Author Comment

by:TommySzalapski
Comment Utility
came* up with
0
 
LVL 30

Accepted Solution

by:
SiddharthRout earned 500 total points
Comment Utility
Tommy

If you paste this code in a module then it will give you the name of all the pdf files that are opened. I am still working on how to get the path.

Sid

Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type

Public Declare Function GetWindow Lib "user32" _
(ByVal hwnd As Long, ByVal wCmd As Long) As Long

Public Declare Function GetWindowRect Lib "user32" _
(ByVal hwnd As Long, lpRect As RECT) As Long

Public Declare Function EnumChildWindows Lib "user32" _
(ByVal hWndParent As Long, ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long

Public Declare Function GetWindowTextLength Lib "user32" _
Alias "GetWindowTextLengthA" (ByVal hwnd As Long) As Long

Public Declare Function EnumWindows Lib "user32.dll" _
(ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long

Public Declare Function IsIconic Lib "user32" (ByVal hwnd As Long) As Long

Public Declare Function GetDesktopWindow Lib "user32" () As Long

Public Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" _
(ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long

Public Declare Function GetClassName Lib "user32" Alias "GetClassNameA" _
(ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long

Public Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long

Public Declare Function GetWindowLW Lib "user32" Alias "GetWindowLongA" _
(ByVal hwnd As Long, ByVal nIndex As Long) As Long

#Const WinVar = True
#If WinVar Then
    Function FindWindowLike(hWndArray() As Variant, ByVal hWndStart As Variant, _
    WindowText As String, Classname As String, ID) As Integer
    Dim hwnd, r
    Static level
    Static iFound
#ElseIf Win32 Then
    Function FindWindowLike(hWndArray() As Long, ByVal hWndStart As Long, _
    WindowText As String, Classname As String, ID) As Long

    Dim hwnd As Long, r As Long
    
    '~~> Hold the level of recursion:
    Static level As Long
    '~~> Hold the number of matching windows:
    Static iFound As Long
#ElseIf Win16 Then
    Function FindWindowLike(hWndArray() As Integer, ByVal hWndStart As Integer, _
    WindowText As String, Classname As String, ID) As Integer
    Dim hwnd As Integer, r As Integer
    '~~> Hold the level of recursion:
    Static level As Integer
    '~~> Hold the number of matching windows:
    Static iFound As Integer
#End If

Dim sWindowText As String, sClassname As String, sID
'~~> Initialize if necessary:
If level = 0 Then
    iFound = 0
    ReDim hWndArray(0 To 0)
    If hWndStart = 0 Then hWndStart = GetDesktopWindow()
End If

'~~> Increase recursion counter:
level = level + 1

'~~> Get first child window:
hwnd = GetWindow(hWndStart, GW_CHILD)
Do Until hwnd = 0
    DoEvents
    '~~> Search children by recursion:
    r = FindWindowLike(hWndArray(), hwnd, WindowText, Classname, ID)
    '~~> Get the window text and class name:
    sWindowText = Space(255)
    r = GetWindowText(hwnd, sWindowText, 255)
    sWindowText = Left(sWindowText, r)
    sClassname = Space(255)
    r = GetClassName(hwnd, sClassname, 255)
    sClassname = Left(sClassname, r)
    '~~> If window is a child get the ID:
    If GetParent(hwnd) <> 0 Then
        r = GetWindowLW(hwnd, GWL_ID)
        sID = CLng("&H" & Hex(r))
    Else
        sID = Null
    End If
    '~~>  Check that window matches the search parameters:
    If sWindowText Like WindowText And sClassname Like Classname Then
        If IsNull(ID) Then
            '~~>  If find a match, increment counter and
            '~~>  add handle to array:
            iFound = iFound + 1
            ReDim Preserve hWndArray(0 To iFound)
            hWndArray(iFound) = hwnd
        ElseIf Not IsNull(sID) Then
            If CLng(sID) = CLng(ID) Then
                '~~> If find a match increment counter and
                '~~>  add handle to array:
                iFound = iFound + 1
                ReDim Preserve hWndArray(0 To iFound)
                hWndArray(iFound) = hwnd
            End If
        End If
    End If
    '~~> Get next child window:
    hwnd = GetWindow(hwnd, GW_HWNDNEXT)
Loop
'~~> Decrement recursion counter:
level = level - 1
'~~> Return the number of windows found:
FindWindowLike = iFound
End Function

Public Function EnumChildProc(ByVal hwnd As Long, ByVal lParam As Long) As Long
    Dim Length As Long
    Dim sName As String
    Length = GetWindowTextLength(hwnd) + 1
    If Length > 1 Then
        sName = Space(Length)
        GetWindowText hwnd, sName, Length
        If InStr(1, sName, ".pdf", vbTextCompare) Then Debug.Print Left(sName, Length - 1)
    End If
    EnumChildProc = 1
End Function

Public Sub Sample()
    Static hWnds() As Variant
    r = FindWindowLike(hWnds(), 0, "*.pdf", "*", Null)
    EnumChildWindows hWnds(r), AddressOf EnumChildProc, ByVal 0&
End Sub

Open in new window

0
 
LVL 30

Expert Comment

by:SiddharthRout
Comment Utility
Sorry you need to run the last Sub Sample. Also press Ctl G to see immediate window where the output will be generated.

Sid
0
 
LVL 37

Author Comment

by:TommySzalapski
Comment Utility
Did you ever find a way to get the path?
0
 
LVL 30

Expert Comment

by:SiddharthRout
Comment Utility
No Tommy. Even when I close the 1st pdf file, the Acro Exe still refers to the first file!!! I could get the name of all the pdf's though...

Still trying to figure out other way...

Sid
0
 
LVL 37

Author Comment

by:TommySzalapski
Comment Utility
That's because you are looking at the command that started Acrobat.
If you start Acrobat by itself and then open pdfs, the command shell string won't give you any file at all.
0
 
LVL 30

Expert Comment

by:SiddharthRout
Comment Utility
Exactly but the API that I know can get the path of the Exe and that is not that we are looking at. So I am still thinking of an alternative to get the paths from the window name.

Sid
0
 
LVL 30

Expert Comment

by:SiddharthRout
Comment Utility
OPTION 1

Ok I have been experimenting and I got stuck. But I am close...

Here is an example that I am trying to incorporate for just one pdf file. If this works then I will be able to get the path for all the PDF in the loop.

Under the file menu of Adobe is the submenu "properties" I am able to click that. I am just stuck as to how to retrieve the value of "Location". See snapshot.

Sid

Code Used

Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As Long) As Long

Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, _
ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long

Const WM_GETTEXT = &HD
Const WM_GETTEXTLENGTH = &HE

Private Sub Command_Click()
    Dim parenthwnd As Long
    
    '~~> Testing with 1.pdf
    parenthwnd = FindWindow(vbNullString, "1.pdf - Adobe Reader")
    
    '~~> Bringing the window to the foreground
    SetForegroundWindow parenthwnd
    
    '~~> Stimulating Alt F + R
    SendKeys "%F", True
    SendKeys "R", True
    
    '~~> Getting the handle of the "Document Properties"
    parenthwnd = FindWindow(vbNullString, "Document Properties")
    
    '~~> The code works till here
End Sub

Open in new window

Untitled.jpg
0
 
LVL 30

Assisted Solution

by:SiddharthRout
SiddharthRout earned 500 total points
Comment Utility
OPTION 2 (unreliable method as it depends whether the user has enabled Recent List or not)

Getting the handle of the menu and then the handle of Recent List. Each pdf file will be in location 1 of it's recent list (See Picture above)

Sid
0
 
LVL 29

Expert Comment

by:nffvrxqgrcfqvvc
Comment Utility
You mean something like this?
 egl1044-411890.flv
0
 
LVL 37

Author Comment

by:TommySzalapski
Comment Utility
I don't really feel like waiting for an 11MB video to download so I can watch it full screen but if it's getting the path and filename of all pdfs open in Acrobat Reader, then yes. I'm not really looking for a video though, I'm looking for code that I can run. Can you post the code you used?
0
 
LVL 37

Author Comment

by:TommySzalapski
Comment Utility
Yes. That looks like what I want. Can you post the code, please?
0
 
LVL 30

Expert Comment

by:SiddharthRout
Comment Utility
Even I am curious to see that code.

Sid
0
 
LVL 37

Author Comment

by:TommySzalapski
Comment Utility
egl1044?
0
Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

 
LVL 30

Expert Comment

by:SiddharthRout
Comment Utility
How do I contact Southmod?

Sid
0
 
LVL 30

Expert Comment

by:SiddharthRout
Comment Utility
Much appreciated modus_in_rebus :)

Sid
0
 
LVL 29

Expert Comment

by:nffvrxqgrcfqvvc
Comment Utility
Did you get a solution for this yet?
0
 
LVL 37

Author Comment

by:TommySzalapski
Comment Utility
No. I've found programs that find all the open file handles and list them (Process Hacker etc), but none of them have an API that let me get the list programmatically. This may be my best option so far.
I've also been pointed to some Windows API calls beginning with NtQuerySystemInformation but I haven't had the time to try to get them working in VBScript yet.
0
 
LVL 29

Expert Comment

by:nffvrxqgrcfqvvc
Comment Utility
Do you want me to create library that you can use from script?
0
 
LVL 37

Author Comment

by:TommySzalapski
Comment Utility
Sure.
0
 
LVL 29

Expert Comment

by:nffvrxqgrcfqvvc
Comment Utility
Here play with this version until I finish the library. Let me know if you find bugs... If you need more explanation of how to use the library let me know example script version in code section.





'	AdobePDFX32.DLL
'	Version 1.0 Beta
'	egl1044
'        acroPDF.VBS

Dim AcrobatCore			'// AdobePDFX32
Dim AcroProcess			'// IAcrobatProcess
Dim AcroProcessCol		'// IAcrobatProcessCollection
Dim AcroPDFFile			'// IAcrobatPDFFile
Dim AcroPDFFileCol		'// IAcrobatPDFFileCollection
Dim AcroProcessRef		'// IAcrobatProcess (user referenced)


Sub SetObjectInitialStates
	Set AcrobatCore		= Nothing
	Set AcroProcess		= Nothing
	Set AcroProcessCol	= Nothing
	Set AcroPDFFile		= Nothing
	Set AcroPDFFileCol	= Nothing
	Set AcroProcessRef 	= Nothing
End Sub

'// 	Set all object variables to Nothing
Call SetObjectInitialStates


'//	Create Core object
Set AcrobatCore = CreateObject("AdobePDFX32.Core")

'//	Get a collection of Acrobat processes
Set AcroProcessCol = AcrobatCore.GetAcrobatProcesses

'//	Enumerate the IAcrobatProcessCollection collection
'//	There can be atleast two available, the IAcrobatProcess interface
'//		that you should reference should have a valid MainWindowHandle
'//		property not equal to zero.

Msgbox "Acrobat Reader Process Count: " & AcroProcessCol.Count
For Each AcroProcess In AcroProcessCol

	'// Reference the interface on condition.

	If AcroProcess.MainWindowHandle <> 0 Then

		Set AcroProcessRef = AcroProcess
		
		Exit For
	End If

Next


'// Display the IAcrobatProcess Interface referenced above.
If Not AcroProcessRef Is Nothing Then

	Msgbox ( AcroProcessRef.Name & vbCrlf & AcroProcessRef.ProcessId & vbCrlf & AcroProcessRef.MainWindowHandle)

End If

'// Obtain the list of open PDF files 
If Not AcroProcessRef Is Nothing Then

	'// Get a collection of IAcrobatPDFFile
	Set AcroPDFFileCol = AcrobatCore.GetAcrobatPDFFiles(AcroProcessRef)
	

	'// Enumerate the IAcrobatPDFFileCollection collection
	Msgbox "Acrobat Reader Open PDF Count: " & AcroPDFFileCol.Count
	For Each AcroPDFFile In AcroPDFFileCol
		
		'// Display the FullName of each opened PDF document.

		Msgbox ( AcroPDFFile.FullName )
	Next
	
End If

Open in new window

AdobePDFX32.dll
0
 
LVL 29

Expert Comment

by:nffvrxqgrcfqvvc
Comment Utility
Pleased to announce Acroof 1.0.0.0 Release Enjoy Tom

changes_acroof
Acroof Revision History

Acroof 1.0.0.0: 4/5/2011      - Stable: XP,Vista,Windows 7
- New: Added new detection scheme
- New: Added Win32 file path support for Vista or later operating systems.
- New: Added Win32 file path support for Window XP operating system.
- New: Added Win32 file path support for network paths.
- New: Added MUP and LanmanRedirector support in Windows XP operating system.
- New: Added runtime functionality verification
- Fix: Removed various class/interfaces no longer required with the new detection scheme.
- Some minor changes and improvements
Dim oCore       '//	Acroof.Core
  Dim oFile       '//	IPDFFile
  Dim oFileCol    '//	IPDFFileCollection
  
  Set oCore = CreateObject("Acroof.Core")

  If oCore.IsCoreSupported Then
    Set oFileCol = oCore.GetOpenPDFFiles
      If oFileCol.Count <> 0 Then
        For Each oFile In oFileCol
          MsgBox oFile.FullName
        Next
      Else
        MsgBox "No files opened"
      End If
  End If

Open in new window

Acroof-1000.zip
0
 
LVL 37

Author Comment

by:TommySzalapski
Comment Utility
I really appreciate all the time you put into this, but I think you missed the part where I said I only have Acrobat Reader and so I can't call the Acrobat API at all.

I set a reference to your dll and it lets me declare
Dim oCore As Acroof.Core
So the library is running and connected but when it gets to the line
Set oCore = CreateObject("Acroof.Core")
It says it cannot create the object (which is what I expected to see)

If I set references to every Acro* thing in there it still won't let this line work (which I tried from your other post just to prove the point). It gives the same error.
Set AcrobatCore = CreateObject("AdobePDFX32.Core")

I'm sorry you spent so much time on this, but I still can't call any Acrobat ATIs.
0
 
LVL 29

Expert Comment

by:nffvrxqgrcfqvvc
Comment Utility
It doesn't use the acrobat type library only internal windows API... You have to run RegSvr32 on acroof.dll before you can use it
0
 
LVL 37

Author Comment

by:TommySzalapski
Comment Utility
I see. Yes that works nicely. Thanks a lot. The only problem is that it requires Admin privaleges to run, but since that wasn't specified in the original question, I won't count it against you. Post the code that you are using in the current dll, please, and then I'll close this out.
0
 
LVL 37

Author Comment

by:TommySzalapski
Comment Utility
Admin privaleges to set up I mean, not run. Once set up, I know it will run for anybody.
0
 
LVL 29

Expert Comment

by:nffvrxqgrcfqvvc
Comment Utility
<< The only problem is that it requires Admin privaleges to run, but since that wasn't specified in the original question, I won't count it against you. >>

1) Yes and No. You can use per user registration without admin rights and still use the library
2) Count it against me? If you mean points they don't really concern me, I come here to help others
3) The source code not going to happen because it's not a couple lines of code
0
 
LVL 37

Author Comment

by:TommySzalapski
Comment Utility
You can use per user registration without admin rights and still use the library
I had to elevate to admin to register the dll at all.

The source code not going to happen because it's not a couple lines of code
Then why did you bother writing it? This site is supposed to be for experts to freely exchange knowledge. If I wanted a third party app, then you could have read that I had one already. Your library is very inflexible. What if I want it to do something slightly different later? The original question specifically asked for a code solution.
0
 
LVL 29

Expert Comment

by:nffvrxqgrcfqvvc
Comment Utility
< Do you want me to create library that you can use from script?  >
TommySzalapski: Sure.


You can delete this question if it's not up to your standards
0
 
LVL 37

Author Comment

by:TommySzalapski
Comment Utility
I am a very active member on this site. I would prefer if you ask me if I did something on purpose before assuming I messed something up. I needed a code solution (as a library or not, but code nonetheless) and Sid got the closest to providing one. I accepted all the most useful posts and spent some time choosing them. Now I have to do it all again.
0
 
LVL 37

Author Comment

by:TommySzalapski
Comment Utility
I see. His post wasn't asking for a response, but I understand the notion of the system flagging it. In the future, I will make sure mine is the last post when I close.
0

Featured Post

Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Inserting page numbers in Portable Document Files not only enhances manageability but also makes them look professional. With numbered pages, the file appears more organized and it becomes easier to search for a particular page. The size and the vol…
The Adobe PDF proprietary file format is recognized as secure and formulated. But these PDF files are also prone to corruption and any external threat like virus attacks, improper storage can hit PDF file integrity.This type of damages can make cruc…
This Micro Tutorial will demonstrate the scrolling table in Microsoft Excel using the INDEX function.
This Micro Tutorial will demonstrate how to create pivot charts out of a data set. I also added a drop-down menu which allows to choose from different categories in the data set and the chart will automatically update.

772 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now