使用 pywin32 从选定的资源管理器窗口返回所有文件的列表

Posted

技术标签:

【中文标题】使用 pywin32 从选定的资源管理器窗口返回所有文件的列表【英文标题】:Return a list of all files from the selected Explorer Window with pywin32 【发布时间】:2017-05-13 05:56:37 【问题描述】:

我目前在 Python 3 上使用 Win32 api。对于窗户检查,我使用的是 Microsoft Inspect Tool。目前,我有以下代码枚举所有窗口:

def getSelectedFile():

    def callback(handle, hwnds):
        print(str(handle) + " - class name: " + win32gui.GetClassName(handle) + "-- name: " + win32gui.GetWindowText(handle))
        return True

    hwnd = win32gui.GetForegroundWindow()
    if hwnd:
        if win32gui.GetClassName(hwnd) == 'CabinetWClass':  # this is the main explorer window
            win32gui.EnumChildWindows(hwnd, callback, None)

这将输出以下内容:

19269320 - class name: BrowserFrameGripperClass-- name: 
526990 - class name: WorkerW-- name: 
395922 - class name: ReBarWindow32-- name: 
13371224 - class name: TravelBand-- name: 
2559382 - class name: ToolbarWindow32-- name: 
11076870 - class name: Address Band Root-- name: 
2230638 - class name: msctls_progress32-- name: 
7930970 - class name: Breadcrumb Parent-- name: 
6292500 - class name: ToolbarWindow32-- name: Address: Libraries\Pictures
8980342 - class name: ToolbarWindow32-- name: 
9568934 - class name: UniversalSearchBand-- name: 
11403790 - class name: Search Box-- name: 
7407762 - class name: SearchEditBoxWrapperClass-- name: 
23266054 - class name: DirectUIHWND-- name: 
7078564 - class name: ShellTabWindowClass-- name: Pictures
11732514 - class name: DUIViewWndClassName-- name: 
12584158 - class name: DirectUIHWND-- name: 
1118546 - class name: CtrlNotifySink-- name: 
987636 - class name: NamespaceTreeControl-- name: Namespace Tree Control
8193258 - class name: Static-- name: Namespace Tree Control
24314574 - class name: SysTreeView32-- name: Tree View
21103510 - class name: CtrlNotifySink-- name: 
1642968 - class name: Shell Preview Extension Host-- name: Shell Preview Extension Host
1577368 - class name: CtrlNotifySink-- name: 
2036036 - class name: SHELLDLL_DefView-- name: ShellView
24380214 - class name: DirectUIHWND-- name: 
1969552 - class name: CtrlNotifySink-- name: 
594366 - class name: ScrollBar-- name: 
987466 - class name: CtrlNotifySink-- name: 
17827752 - class name: ScrollBar-- name: 
2035978 - class name: CtrlNotifySink-- name: 
4851916 - class name: Button-- name: Save
13174848 - class name: CtrlNotifySink-- name: 
7145486 - class name: Button-- name: Cancel
1509810 - class name: WorkerW-- name: 
12781114 - class name: ReBarWindow32-- name: 
11405468 - class name: ToolbarWindow32-- name: 
1315080 - class name: msctls_statusbar32-- name: 

这很棒。但也请注意,通过查看检查工具(如图所示),这些对象的 frameworkId 仅为“Win32”。

在检查器中,我注意到一些对象具有不同的名为“DirectUI”的 frameworkId,并且它们似乎没有从 EnumChildWindows 函数中显示出来。这是一个问题,因为包含所有文件的对象实际上称为“项目视图”窗格,并且是“DirectUI”(参见第二张图)。所以它甚至没有被检测到。如果未检测到,如何读取其中的所有文件?我知道名字在那里,因为你可以在树中看到它们(在下图中)

如何让 Win32API 与 DirectUI 一起工作以读取文件名? 有没有更简单的方法来检索所有文件的名称列表?

【问题讨论】:

我建议使用 Shell COM API 而不是摆弄 windows。一旦有了IFolderView interface 的实例,就可以使用IFolderView::Item() 方法读取文件/文件夹列表。这是一些C++ code to obtain the IFolderView interface from an explorer window。您必须自己将其转换为 Python 代码。这是一些python code for getting started with the shell objects。 您依赖于实施细节。不。使用 API。 @DavidHeffernan 你建议我从哪里开始学习 Pyton 的 Windows Shell?有什么资源/网站可以推荐吗?谢谢。 【参考方案1】:

shell有一个dedicated COM API这样的东西,可以通过pywin32访问。

这是我想出的工作代码:

import os
import sys
import win32con
import win32api
import win32gui
import win32com.client
import pythoncom
from win32com.shell import shell, shellcon

# Get list of paths from given Explorer window or from all Explorer windows.
def get_explorer_files( hwndOfExplorer = 0, selectedOnly = False ):
    paths = []

    # Create instance of IShellWindows (I couldn't find a constant in pywin32)
    CLSID_IShellWindows = "9BA05972-F6A8-11CF-A442-00A0C90A8F39"
    shellwindows = win32com.client.Dispatch(CLSID_IShellWindows)

    # Loop over all currently open Explorer windows
    for window in shellwindows:
        # Skip windows we are not interested in.
        if hwndOfExplorer != 0 and hwndOfExplorer != window.HWnd:
            continue

        # Get IServiceProvider interface
        sp = window._oleobj_.QueryInterface( pythoncom.IID_IServiceProvider )

        # Query the IServiceProvider for IShellBrowser
        shBrowser = sp.QueryService( shell.SID_STopLevelBrowser, shell.IID_IShellBrowser )

        # Get the active IShellView object
        shView = shBrowser.QueryActiveShellView()

        # Get an IDataObject that contains the items of the view (either only selected or all). 
        aspect = shellcon.SVGIO_SELECTION if selectedOnly else shellcon.SVGIO_ALLVIEW
        items = shView.GetItemObject( aspect, pythoncom.IID_IDataObject )

        # Get the paths in drag-n-drop clipboard format. We don't actually use 
        # the clipboard, but this format makes it easy to extract the file paths.
        # Use CFSTR_SHELLIDLIST instead of CF_HDROP if you want to get ITEMIDLIST 
        # (aka PIDL) format, but you can't use the simple DragQueryFileW() API then. 
        data = items.GetData(( win32con.CF_HDROP, None, pythoncom.DVASPECT_CONTENT, -1, pythoncom.TYMED_HGLOBAL ))

        # Use drag-n-drop API to extract the individual paths.
        numPaths = shell.DragQueryFileW( data.data_handle, -1 )
        paths.extend([
            shell.DragQueryFileW( data.data_handle, i ) \
                for i in range( numPaths )
        ])

        if hwndOfExplorer != 0:
            break

    return paths

try:
    # Use hwnd value of 0 to list files of ALL explorer windows...
    hwnd = 0  
    # ... or restrict to given window:
    #hwnd = win32gui.GetForegroundWindow()
    selectedOnly = False
    print( *get_explorer_files( hwnd, selectedOnly ), sep="\n" )
except Exception as e:
    print( "ERROR: ", e )

哇,这是一个很好的谜题(因为我实际上是一个 C++ 人)!

要了解这些内容,我建议研究original MSDN documentation,然后尝试将其映射到pywin32 code。

Shell API(以及一般的 COM)一开始可能会有点让人不知所措,但调整现有示例代码通常并不难。一个很好的来源是blog of Raymond Chen。

对于pywin32示例,pywin32安装的这个文件夹中有一些demo:

Lib\site-packages\win32comext\shell\demos\

【讨论】:

以上是关于使用 pywin32 从选定的资源管理器窗口返回所有文件的列表的主要内容,如果未能解决你的问题,请参考以下文章

如果某个任务正在任务管理器中运行,我如何在 python 中使用 pywin 或 win32com.client 检查?

如何从 Eclipse 插件中获取包资源管理器中的选定节点

如何使用 pywin32 在 Python 中获取特定应用程序窗口的句柄?

python 使用WMI和pywin32获取窗口温度区域

一次使用pywin32学到的知识

如何将键盘事件发送到 Pywin32 中的不同窗口?