I have a python console app which runs on WIndows. It also has a web GUI which runs in the browser. In my GUI I have buttons which when clicked should open a folder in the Windows file explorer. So, when the button is clicked, I send a command from the browser to the backend (the python console app) which in turn should open the needed file.
The problem is that the explorer’s window appears under the browser’s window (though in front of the console’s window):
I’ve found out that you can bring the window to front using win32 API. Though, you need to know the HWND - the window handle (ID).
I’m opening the file explorer using subprocess, which provides the PID of the spawned process. Knowing the PID we can find all the windows of the process like so:
import win32gui
import win32process
import subprocess
import time
def enumHandler(hwnd, lParam):
thread_id, process_id = win32process.GetWindowThreadProcessId(hwnd)
if process_id == opened_pid:
print("Found a window:")
print(hwnd, win32gui.GetWindowText(hwnd))
with subprocess.Popen("notepad.exe") as proc:
opened_pid = proc.pid
time.sleep(2)
win32gui.EnumWindows(enumHandler, None)
The problem with my approach
This works well if the command is "notepad.exe", for example. However, it won’t find any windows if I try the command that I want, which is r"explorer path\to\folder".
I suspect that with a command like this I’m getting the wrong PID from proc.pid. Therefore, my script cannot find any windows associated with it.
Running explorer "path\to\folder" will reuse the session’s main Explorer process, unless you enable the option to “launch folder windows in a separate process”. Even with the latter option, Explorer won’t use an arbitrarily spawned instance. It uses the DCOM launcher service to respawn itself, and the first instance exits immediately.
I’d require the session to have a desktop shell (not headless) and require this shell to be “explorer.exe”. This includes the vast majority of Windows installations, but it’s trivial to check, so it can’t hurt. The function GetShellWindow() returns the handle of the shell window, if there is one.
For the enumeration of top-level windows, if you know it’s a window that’s owned by Explorer, it should have at least the base folder name in the window title.
Thank you for this creative solution! Unfortunately, as of now it fails for my app with the following error: error: (5, 'OpenProcess', 'Access is denied.')
I googled a little and some people suggest using PROCESS_QUERY_LIMITED_INFORMATION, which your solution already does. I will try to investigate further why this happens.
There’s a window in your desktop session that’s owned by a process that restricts access. You can modify the function to continue to the next window. For example:
import pywintypes
def enum_func(hwnd, param):
if param['hwnd'] is None:
try:
path = os.path.normcase(get_window_process_path(hwnd))
except pywintypes.error:
return True
if path == param['path']:
text = win32gui.GetWindowText(hwnd)
if os.path.normcase(os.path.basename(text)) == param['text']:
param['hwnd'] = hwnd
return True