找回密码
 加入
搜索
查看: 2070|回复: 2

[系统综合] 【已解决】如何在应用程序窗口获取已打开文件的路径?

[复制链接]
发表于 2017-2-9 22:45:39 | 显示全部楼层 |阅读模式
本帖最后由 wondermore 于 2017-2-15 21:23 编辑

比如我用PDF Reader打开了2个PDF文件,复制到剪贴板一段文字,如何在复制文字的同时获取该PDF文档的路径?用_WinAPI_GetProcessCommandLine后只可获取到触发PDF Reader打开的第一个文档的位置,第2个文档的位置获取不到。
#include <WinAPIFiles.au3>
#include <WinAPI.au3>
#include <Clipboard.au3>
#include <Process.au3>
#include <WinAPIProc.au3>
Func Example()
Local $clipboardOpenWindow,$PID,$id        
$clipboardOpenWindow=_ClipBoard_GetOwner()
$PID = WinGetProcess($clipboardOpenWindow)        
$id=_WinAPI_GetProcessCommandLine($PID) 
MsgBox($MB_SYSTEMMODAL,"",$id)
EndFunc   ;==>Example
新手,苦研2日而不得解,郁闷非常,恳请大侠出手,指点迷津,不胜感激。
发表于 2017-2-10 19:10:05 | 显示全部楼层
它那个浏览记录保存在注册表里了,可以通过枚举和比对获取到当前打开文档的路径.
Local $reg = 'HKEY_CURRENT_USER\Software\Adobe\Acrobat Reader\11.0\AVGeneral\cRecentFiles'
$cls = '[CLASS:AcrobatSDIWindow]'
$a = WinList($cls)
If Not IsArray($a) Then Exit
For $i = 1 To $a[0][0]
        ;MsgBox(0, 'title', $a[$i][0])
        For $ii = 1 To 100
        $key = RegEnumKey($reg, $ii)
        If $key = '' Then ExitLoop
        $val = RegRead($reg & '\' &  $key, 'tDIText')
        If @error Then ContinueLoop
        If StringRegExpReplace($val, '^.+\/|", '') = StringReplace($a[$i][0], ' - Adobe Reader', '') Then 
                MsgBox(0, 'path', StringReplace(StringRegExpReplace($val, '/(\w)/(.+), '\1:\\\2'), '/', '\'))
        EndIf
        Next
Next
 楼主| 发表于 2017-2-15 21:07:28 | 显示全部楼层
本帖最后由 wondermore 于 2017-2-15 21:09 编辑

回复 2# Alam

非常感谢您的帮助,看到的有点晚了。通过刷英文论坛获得了同您一样的思路,已获解决,但正则表达式不及您的完美。但这种方法有局限性就是,在某些软件,比如中国知网CAJViewer中当前新打开的文件不写入注册表,只有关闭后才写入。所以,这部分文档参考了英文论坛的processlistfile function得到了解决。但这个function无法获取到Acrobat的filelist。尚未找到完美的可以合二而一的解决办法。
#NoTrayIcon
#include "WinAPIEx.au3"
#include <WinAPI.au3>
Dim $hTimer = TimerInit()
Dim $aFiles = _ProcessListFiles("wmplayer.exe") ; Get a list of files currently opened by the process
ConsoleWrite("+>Took " & Round(TimerDiff($hTimer)) & " milliseconds" & @CRLF)
#include <Array.au3>
_ArrayDisplay($aFiles)
Func _ProcessListFiles($vProcess, $nMaxFiles = 1000)
    Static Local $aPrivilege = DllCall("ntdll.dll", "int", "RtlAdjustPrivilege", "int", 20, "int", 1, "int", 0, "int*", 0)
    Local $nProcessId = ProcessExists($vProcess), $aRet
    Static Local $hCurrentProcess = _WinAPI_GetCurrentProcess()
    Local $aHandles = _WinAPI_EnumProcessHandles($nProcessId)
    Local $hObject, $aFiles[$nMaxFiles+1], $sPath
    Local $hProcess = _WinAPI_OpenProcess(0x0040, 0, $nProcessId, True)
    For $i = 1 To $aHandles[0][0] Step 1
        $hObject = _WinAPI_DuplicateHandle($hProcess, $aHandles[$i][0], $hCurrentProcess, 0, False, $DUPLICATE_SAME_ACCESS)
        If Not $hObject Then ContinueLoop
        If __IsFileObject($hObject) Then
            If $aHandles[$i][3] = 0x00120189 Or $aHandles[$i][3] = 0x0012019f Or $aHandles[$i][3] = 0x00100000 Or $aHandles[$i][3] = 0x001F01FF Then
                $sPath = __FileObjectPath_UD($hObject)
            Else
                $sPath = __FileObjectPath($hObject)
            EndIf
            _WinAPI_CloseHandle($hObject)
            If FileExists($sPath) Then
                For $n = 1 To $aFiles[0]
                    If $aFiles[$n] = $sPath Then
                        $sPath = 0
                        ExitLoop
                    EndIf
                Next
                If $sPath Then
                    $aFiles[0] += 1
                    $aFiles[$aFiles[0]] = $sPath
                    If $aFiles[0] >= $nMaxFiles Then ExitLoop
                EndIf
            EndIf
        EndIf
    Next
    ReDim $aFiles[$aFiles[0]+1]
    Return $aFiles
EndFunc
Func __IsFileObject(ByRef $hObject)
    Static Local $tPOTI = DllStructCreate('ushort;ushort;ptr;byte[128]'), $pData, $Length, $tString
    Local $aRet = DllCall("ntdll.dll", 'uint', 'NtQueryObject', 'ptr', $hObject, 'uint', 2, 'ptr', DllStructGetPtr($tPOTI), 'ulong', DllStructGetSize($tPOTI), 'ptr', 0)
    If @error Or $aRet[0] Then Return
    $pData = DllStructGetData($tPOTI, 3)
    If Not $pData Then Return
    $Length = DllCall("kernel32.dll", 'int', 'lstrlenW', 'ptr', $pData)
    If @error Or Not $Length[0] Then Return
    $Length = $Length[0]
    $tString = DllStructCreate('wchar[' & ($Length + 1) & ']', $pData)
    If @error Then Return
    Return (DllStructGetData($tString, 1) == "File")
EndFunc
Func __FileObjectPath_UD($hObject)
    Static Local $tStruct = DllStructCreate("char[255];")
    Local $aDrive = DriveGetDrive("ALL"), $sPath
    Local $aDrivesInfo[UBound($aDrive) - 1][2]
    For $I = 0 To UBound($aDrivesInfo) - 1
        $aDrivesInfo[$I][0] = $aDrive[$I + 1]
        DllCall("kernel32.dll", "dword", "QueryDosDevice", "str", $aDrivesInfo[$I][0], "ptr", DllStructGetPtr($tStruct), "dword", 255)
        $aDrivesInfo[$I][1] = DllStructGetData($tStruct, 1)
    Next
    $sDeviceStr = _ObjectGetNameUD_Threaded($hObject)
    If Not $sDeviceStr Then Return
    For $y = 0 To UBound($aDrivesInfo) - 1
        If StringLeft($sDeviceStr, StringLen($aDrivesInfo[$y][1])) = $aDrivesInfo[$y][1] Then
            $sPath = StringUpper($aDrivesInfo[$y][0]) & StringTrimLeft($sDeviceStr, StringLen($aDrivesInfo[$y][1]))
        EndIf
    Next
    Return $sPath
EndFunc
Func __FileObjectPath($hObject)
    Static Local $tStruct = DllStructCreate("char[255];")
    Local $aDrive = DriveGetDrive("ALL"), $sPath
    Local $aDrivesInfo[UBound($aDrive) - 1][2]
    For $I = 0 To UBound($aDrivesInfo) - 1
        $aDrivesInfo[$I][0] = $aDrive[$I + 1]
        DllCall("kernel32.dll", "dword", "QueryDosDevice", "str", $aDrivesInfo[$I][0], "ptr", DllStructGetPtr($tStruct), "dword", 255)
        $aDrivesInfo[$I][1] = DllStructGetData($tStruct, 1)
    Next
    Local Static $tPOTI = DllStructCreate("ushort Length;ushort MaximumLength;ptr Buffer;wchar Reserved[260];"), $sDeviceStr, $vSolid = False
    DllCall("ntdll.dll", "ulong", "NtQueryObject", "ptr", $hObject, "int", 1, "ptr", DllStructGetPtr($tPOTI), "ulong", DllStructGetSize($tPOTI), "ulong*", "")
    $sDeviceStr = DllStructGetData(DllStructCreate("wchar[" & Ceiling(DllStructGetData($tPOTI, "Length") / 2) & "];", DllStructGetData($tPOTI, "buffer")), 1)
    For $y = 0 To UBound($aDrivesInfo) - 1
        If StringLeft($sDeviceStr, StringLen($aDrivesInfo[$y][1])) = $aDrivesInfo[$y][1] Then
            $sPath = StringUpper($aDrivesInfo[$y][0]) & StringTrimLeft($sDeviceStr, StringLen($aDrivesInfo[$y][1]))
        EndIf
    Next
    Return $sPath
EndFunc
; ==============================================================================================
; Func _ObjectGetNameUD_Threaded($hObject)
;
; Gets the Name associated with a Kernel Object Handle.
;  Most useful for 'File' Types
;
; This differs from _ObjectGetNameUD() in that it launches a thread to try and get the name
; If the thread times-out, it is killed.
;
; This is a workaround for the NamedPipes issue - often these types of Objects will be
; locked in reads/writes/waits for long periods of time and cause any type of querying
; attempts on the Object to lockup indefinitely.
;
; Returns:
;  Success: String representing the name of the Object
;  Failure: "" with @error set
;
; Author: Ascend4nt
; ==============================================================================================
Func _ObjectGetNameUD_Threaded($hObject)
    Local $OGNT_CODE
    ;~     $aRet=DllCall("ntdll.dll", "long", "NtQueryObject", "handle", $hObject, "int", 1, _
    ;~         "ptr", DllStructGetPtr($stBuffer), "ulong", DllStructGetSize($stBuffer), "ulong*", 0)
    If @AutoItX64 Then
        $OGNT_CODE='0x4831C04889CE483906742D483946087427504883EC2049C7C1FF7F00004C8D461848C7C201000000488B4E08488B1EFFD34883C428894610C3'
    Else
        $OGNT_CODE='0x5589E531C08B750839067421394604741C50B8FF7F0000508D461050B801000000508B4604508B1EFFD38946085DC3'
    EndIf
    Local $aRet, $sStr = "", $iErr = 0, $iExt = 0, $stData
    Local $stCode, $iCodeSize, $pThreadMem, $hThread = 0
    If Not IsPtr($hObject) Or $hObject = 0 Then Return SetError(1,0,"")
    $iCodeSize = BinaryLen($OGNT_CODE)
    $aRet = DllCall("kernel32.dll", "ptr", "GetModuleHandleA", "str", "ntdll.dll")
    If @error Then Return SetError(2, @error, "")
    If $aRet[0] = 0 Then Return SetError(3, 0, "")
    $aRet = DllCall("kernel32.dll", "ptr", "GetProcAddress", "handle", $aRet[0], "str", "NtQueryObject")
    If @error Then Return SetError(2, @error, "")
    If $aRet[0] = 0 Then Return SetError(3, 0, "")
    ;$pNTQO = $aRet[0]
    ; Data to receive (must be kept alive while thread exists!)
    $stData = DllStructCreate("ptr NTQO;handle Object;ulong StatusRet;ulong StructPad;ushort Length;ushort MaximumLength;ptr Buffer;" & "byte [32768];")
    DllStructSetData($stData, "Object", $hObject)
    DllStructSetData($stData, "NTQO", $aRet[0])    ; $pNTQO
    $aRet = DllCall("kernel32.dll", "ptr", "VirtualAllocEx", "handle", -1, _
        "ptr", 0, "ulong_ptr", $iCodeSize, "dword", 0x1000, "dword", 0x40)
    If @error Then Return SetError(2, @error, "")
    If $aRet[0] = 0 Then Return SetError(3, 0, "")
    $pThreadMem = $aRet[0]
    ConsoleWrite("...Return from VirtualAllocEx = " & $pThreadMem & @CRLF)
    ; Code area (set in newly allocated space)
    $stCode = DllStructCreate("byte ["&$iCodeSize&"];", $pThreadMem)
    ; Set the code
    DllStructSetData($stCode, 1, $OGNT_CODE)
    ;MsgBox(0, "Code Set", "Code set at " & $pThreadMem)    ; For setting Breakpoints
    ; Create the Thread - passing a pointer to $stData
    $aRet = DllCall("kernel32.dll", "handle", "CreateThread", "ptr", 0, _
        "ulong_ptr", 0, "ptr", $pThreadMem, "ulong_ptr", DllStructGetPtr($stData), "dword", 0, "dword*", 0)
    If @error Then
        $iExt = @error
        $iErr = 2
        ConsoleWrite("CreateThread error:" & @error & @CRLF)
    ElseIf $aRet[0] = 0 Then
        $iErr = 3
    Else
        ConsoleWrite("...Created Thread, Commencing Wait.."&@CRLF)
        $hThread = $aRet[0]
        ; Wait a suitable amount of time for thread to complete (100 ms here)
        $aRet = DllCall("kernel32.dll", "dword", "WaitForSingleObject", "handle", $hThread, "dword", 100)
        If @error Then
            $iExt = @error
            $iErr = 2
            ConsoleWrite("WaitForSingleObject error: " & @error & @CRLF)
        ; Anything other than WAIT_OBJECT_0 (i.e., success)
        ElseIf $aRet[0] <> 0 Then
            $iErr = -1
            ; Terminate thread - probably was locked up on querying a NamedPipe
            $aRet = DllCall("kernel32.dll", "bool", "TerminateThread", "handle", $hThread, "int", -1)
            ConsoleWrite("..TerminateThread return: " & $aRet[0] & @CRLF)
        Else
            ConsoleWrite("..Checking status return and looking for string.."&@CRLF)
            $iExt = DllStructGetData($stData, "StatusRet")
            ; Check for NTSTATUS return of STATUS_SUCCESS (0)
            If $iExt = 0 Then
                ; Success, let's check and grab the string return
                Local $pStr, $stString
                $pStr = DllStructGetData($stData, "Buffer")
                If $pStr = 0 Then
                    ;ConsoleWrite(@TAB&"-- No Object Name string --"&@CRLF)
                    ;$sStr = ""
                Else
                    $stString = DllStructCreate("wchar ["&(DllStructGetData($stData, "Length")/2)&"];", $pStr)
                    $sStr = DllStructGetData($stString, 1)
                    ;ConsoleWrite(@TAB&"Object Name string = " & $aHandleInfo[$i][6] & @CRLF)
                EndIf
            Else
                $iErr = 3
            EndIf
        EndIf
    EndIf
    ; Now free the memory the code was in
    $aRet = DllCall("kernel32.dll", "bool", "VirtualFreeEx", "handle", -1, "ptr", $pThreadMem, "ulong_ptr", 0, "dword", 0x8000)
    ConsoleWrite("..Return from VirtualFreeEx = " & $aRet[0] & @CRLF)
    _WinAPI_CloseHandle($hThread)
    ; Any errors?
    If $iErr Then Return SetError($iErr, $iExt, "")
    ; Yay, we have a string (or a "" if there was none)
    Return $sStr
EndFunc
您需要登录后才可以回帖 登录 | 加入

本版积分规则

QQ|手机版|小黑屋|AUTOIT CN ( 鲁ICP备19019924号-1 )谷歌 百度

GMT+8, 2024-4-20 04:24 , Processed in 0.085530 second(s), 23 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表