Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
957 views
in Technique[技术] by (71.8m points)

winforms - Return a list of all files from the selected Explorer Window with pywin32

I'm currently on Python 3 using the Win32 api. For window inspection, I'm using the Microsoft Inspect Tool. Currently, I have the following code that enumerates through all the windows:

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)

And this would output the following:

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: LibrariesPictures
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: 

Which is great. But also notice that these objects have a frameworkId of ONLY "Win32" by looking at the Inspect tool (as you can see in the picture).

enter image description here

From the inspector, I noticed that some objects have a different frameworkId named "DirectUI", and they don't seem to show up from the EnumChildWindows function. This is a problem, because the object that contains all the files is actually called "Items View" pane, and it is "DirectUI" (refer to the second picture). So it's not even getting detected. If it's not detected, how can I read all the files inside it? I know that the names are there because you can see them in the tree (in the picture below)

enter image description here

How can I get Win32API to work with DirectUI in order to read the file names?
Is there an easier way of retrieving a list of names of all the files?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The shell has a dedicated COM API for things like this, which can be accessed through pywin32.

Here is working code I've come up with:

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="
" )
except Exception as e:
    print( "ERROR: ", e )

Wow, that was a nice puzzle (as I'm actually a C++ guy)!

To understand that stuff I suggest studying the original MSDN documentation, then try to map that to pywin32 code.

Shell API (and COM in general) can be a little bit overwhelming at first but it's usually not that difficult to adapt existing example code. A great source for that is the blog of Raymond Chen.

For pywin32 samples, there are some demos in this folder of the pywin32 installation:

Libsite-packageswin32comextshelldemos

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...