簡體   English   中英

Python - 在當前 Windows 資源管理器中獲取所選文件的路徑

[英]Python - Get Path of Selected File in Current Windows Explorer

我試圖在 Python 2.7 中做到這一點 我在 C# here 中找到了答案,但我無法在 Python 中重新創建它。 此處建議的答案確實解釋了我所理解的概念,但我不知道如何進行。

基本上我只想標記一個文件,按 Winkey+C 並復制其路徑。 我知道如何做熱鍵部分(pyhk,win32 [RegisterHotKey]),但我的問題是處理文件路徑。

提前致謝!

這需要大量的黑客攻擊,但粗略的解決方案如下:

#!python3
import win32gui, time
from win32con import PAGE_READWRITE, MEM_COMMIT, MEM_RESERVE, MEM_RELEASE, PROCESS_ALL_ACCESS, WM_GETTEXTLENGTH, WM_GETTEXT
from commctrl import LVM_GETITEMTEXT, LVM_GETITEMCOUNT, LVM_GETNEXTITEM, LVNI_SELECTED
import os
import struct
import ctypes
import win32api

GetWindowThreadProcessId = ctypes.windll.user32.GetWindowThreadProcessId
VirtualAllocEx = ctypes.windll.kernel32.VirtualAllocEx
VirtualFreeEx = ctypes.windll.kernel32.VirtualFreeEx
OpenProcess = ctypes.windll.kernel32.OpenProcess
WriteProcessMemory = ctypes.windll.kernel32.WriteProcessMemory
ReadProcessMemory = ctypes.windll.kernel32.ReadProcessMemory
memcpy = ctypes.cdll.msvcrt.memcpy

def readListViewItems(hwnd, column_index=0):
    # Allocate virtual memory inside target process
    pid = ctypes.create_string_buffer(4)
    p_pid = ctypes.addressof(pid)
    GetWindowThreadProcessId(hwnd, p_pid) # process owning the given hwnd
    hProcHnd = OpenProcess(PROCESS_ALL_ACCESS, False, struct.unpack("i",pid)[0])
    pLVI = VirtualAllocEx(hProcHnd, 0, 4096, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE)
    pBuffer = VirtualAllocEx(hProcHnd, 0, 4096, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE)

    # Prepare an LVITEM record and write it to target process memory
    lvitem_str = struct.pack('iiiiiiiii', *[0,0,column_index,0,0,pBuffer,4096,0,0])
    lvitem_buffer = ctypes.create_string_buffer(lvitem_str)
    copied = ctypes.create_string_buffer(4)
    p_copied = ctypes.addressof(copied)
    WriteProcessMemory(hProcHnd, pLVI, ctypes.addressof(lvitem_buffer), ctypes.sizeof(lvitem_buffer), p_copied)

    # iterate items in the SysListView32 control
    num_items = win32gui.SendMessage(hwnd, LVM_GETITEMCOUNT)
    item_texts = []
    for item_index in range(num_items):
        win32gui.SendMessage(hwnd, LVM_GETITEMTEXT, item_index, pLVI)
        target_buff = ctypes.create_string_buffer(4096)
        ReadProcessMemory(hProcHnd, pBuffer, ctypes.addressof(target_buff), 4096, p_copied)
        item_texts.append(target_buff.value)

    VirtualFreeEx(hProcHnd, pBuffer, 0, MEM_RELEASE)
    VirtualFreeEx(hProcHnd, pLVI, 0, MEM_RELEASE)
    win32api.CloseHandle(hProcHnd)
    return item_texts

def getSelectedListViewItem(hwnd):
    return win32gui.SendMessage(hwnd, LVM_GETNEXTITEM, -1, LVNI_SELECTED)

def getSelectedListViewItems(hwnd):
    items = []
    item = -1
    while True:
        item = win32gui.SendMessage(hwnd, LVM_GETNEXTITEM, item, LVNI_SELECTED)
        if item == -1:
            break
        items.append(item)
    return items

def getEditText(hwnd):
    # api returns 16 bit characters so buffer needs 1 more char for null and twice the num of chars
    buf_size = (win32gui.SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0) +1 ) * 2
    target_buff = ctypes.create_string_buffer(buf_size)
    win32gui.SendMessage(hwnd, WM_GETTEXT, buf_size, ctypes.addressof(target_buff))
    return target_buff.raw.decode('utf16')[:-1]# remove the null char on the end

def _normaliseText(controlText):
    '''Remove '&' characters, and lower case.
    Useful for matching control text.'''
    return controlText.lower().replace('&', '')

def _windowEnumerationHandler(hwnd, resultList):
    '''Pass to win32gui.EnumWindows() to generate list of window handle,
    window text, window class tuples.'''
    resultList.append((hwnd, win32gui.GetWindowText(hwnd), win32gui.GetClassName(hwnd)))

def searchChildWindows(currentHwnd,
               wantedText=None,
               wantedClass=None,
               selectionFunction=None):
    results = []
    childWindows = []
    try:
        win32gui.EnumChildWindows(currentHwnd,
                      _windowEnumerationHandler,
                      childWindows)
    except win32gui.error:
        # This seems to mean that the control *cannot* have child windows,
        # i.e. not a container.
        return
    for childHwnd, windowText, windowClass in childWindows:
        descendentMatchingHwnds = searchChildWindows(childHwnd)
        if descendentMatchingHwnds:
            results += descendentMatchingHwnds

        if wantedText and \
            not _normaliseText(wantedText) in _normaliseText(windowText):
                continue
        if wantedClass and \
            not windowClass == wantedClass:
                continue
        if selectionFunction and \
            not selectionFunction(childHwnd):
                continue
        results.append(childHwnd)
    return results

w=win32gui

while True:
    time.sleep(5)
    window = w.GetForegroundWindow()
    print("window: %s" % window)
    if (window != 0):
        if (w.GetClassName(window) == 'CabinetWClass'): # the main explorer window
            print("class: %s" % w.GetClassName(window))
            print("text: %s " %w.GetWindowText(window))
            children = list(set(searchChildWindows(window)))
            addr_edit = None
            file_view = None
            for child in children:
                if (w.GetClassName(child) == 'ComboBoxEx32'): # the address bar
                    addr_children = list(set(searchChildWindows(child)))
                    for addr_child in addr_children:
                        if (w.GetClassName(addr_child) == 'Edit'):
                            addr_edit = addr_child
                    pass
                elif (w.GetClassName(child) == 'SysListView32'): # the list control within the window that shows the files
                    file_view = child
            if addr_edit:
                path = getEditText(addr_edit)
            else:
                print('something went wrong - no address bar found')
                path = ''

            if file_view:
                files = [item.decode('utf8') for item in readListViewItems(file_view)]
                indexes = getSelectedListViewItems(file_view)
                print('path: %s' % path)
                print('files: %s' % files)
                print('selected files:')
                for index in indexes:
                    print("\t%s - %s" % (files[index], os.path.join(path, files[index])))
            else:
                print('something went wrong - no file view found')

所以這樣做是不斷檢查活動窗口是否屬於資源管理器窗口使用的類,然后遍歷子小部件以查找地址欄和文件列表視圖。 然后它從列表視圖中提取文件列表並請求選定的索引。 它還從地址欄中獲取和解碼文本。
在底部,信息然后組合在一起,為您提供完整路徑、文件夾路徑、文件名或它們的任意組合。

我已經用 python3.4 在 windows xp 上測試過這個,但是你需要安裝 win32gui 和 win32 conn 包。

如果您想要 Windows Vista 或更高版本(不確定 Vista),請在此處查看我的答案: https : //stackoverflow.com/a/52959617/8228163 我將修正后的 James Kent 的答案與較新的 Windows 和 Olav 的答案(在該線程上)混合在一起,然后將其與較新的 Windows 一起使用並僅選擇活動窗口。 我也解釋了如何讓它從所有 Windows 資源管理器窗口中獲取文件(奧拉夫自己說的,我只是在我的回答中再次解釋了)。 對於 XP,只需將原始 James Kent 的答案與 Olav 的答案混合即可。 我也在我的回答中更好地解釋了這一點。

可能 OP 不再需要這個了,但也許其他人會需要它 - 我需要它但沒有完整的答案,所以這可能對某人有所幫助。

感謝 Olav 和 James Kent 的答案,因為我會花更多的時間來嘗試找出如何做到這一點(我是 Python/任何語言的初學者 - 只編碼一年,所以它會花費很多時間,也許我必須將它與另一種語言混合)。 再次感謝 OP,感謝他們提出問題並讓合適的人在合適的時間回答! (因為 Olav 在鏈接上引用的來源不再存在)。

希望這有幫助! 干杯!

# Import Statement. import subprocess # Trigger subprocess. subprocess.popen(r'explorer /select,"C:\\path\\of\\folder\\file"'

抱歉,您嘗試實現的目標沒有多大意義,因為您可以擁有多個資源管理器窗口,其中選擇了文件,多個顯示器,甚至在同一台機器上運行的多個終端會話。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM