Python获取当前Windows Exp中选定文件的路径

2024-05-17 04:34:57 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试在python2.7中执行keyboard-shortcut-to-get-path-of-selected-item-in-windows-explorer">this。我在C#here中找到了一个答案,但是在Python中重新创建它时遇到了麻烦。答案表明here确实解释了我理解的概念,但我不知道如何让它继续下去。在

基本上我只想标记一个文件,按Winkey+C并复制它的路径。我知道如何做热键部分(pyhk,win32[RegisterHotKey]),但我的问题是如何处理文件路径。在

提前谢谢!在


Tags: 文件oftopath答案in路径get
3条回答

这需要大量的黑客攻击,但一个粗略的解决方案如下:

#!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')

因此,它要做的是不断检查活动窗口是否属于资源管理器窗口使用的类,然后遍历子窗口小部件以找到地址栏和文件列表视图。然后它从listview中提取文件列表并请求选定的索引。它还可以从地址栏获取并解码文本。
在底部的信息,然后组合起来,给你完整的路径,文件夹路径,文件名或它们的任何组合。在

我已经用python3.4在windowsxp上测试过了,但是您需要安装win32gui和win32conn包。在

很抱歉,您要实现的目标没有多大意义,因为您可以在同一台计算机上运行多个资源管理器窗口(其中选择了文件)、多个显示器甚至多个终端会话。在

如果你想在Windows Vista或更高版本(对Vista不确定),请看我的答案:https://stackoverflow.com/a/52959617/8228163。我把jameskent的正确答案和Olav的答案混合在一起(在这个线程上),我把它与更新的窗口一起工作,只选择活动窗口。我也解释了如何从所有的Windows资源管理器窗口获取文件(Olav自己说过,我只是在我的回答中再次解释过)。对于XP,只需混合原始jameskent的答案和Olav的答案。我也在我的回答中解释得更好。在

也许手术室不再需要这个了,但也许其他人会-我需要它,但没有完整的答案,所以这可能会帮助别人。在

感谢Olav和James Kent给出的答案,因为我会花更多的时间来尝试如何做到这一点(我是一个Python/任何语言begginner—只需编写一年代码,所以需要花费大量时间,也许我必须将其与另一个语言混合使用)。再次感谢你们,感谢你们的行动,感谢你们在正确的时间问了问题,让合适的人来回答!(因为Olav在链接上引用的源不再存在)。在

希望这有帮助!干杯!在

相关问题 更多 >