在Windows11上使用win32gui模块的Python3.10.5中,我想创建一个简单的窗口切换应用程序.首先,我列出了所有正在运行的窗口的hwnd,然后在代码中的其他地方,我使用给定的hwnd切换到其中一个窗口.代码的相关和简化部分如下所示:

def winEnumHandler(hwnd, ctx):
  if win32gui.IsWindowVisible(hwnd):
    windows.append(hwnd)

def switchToWindow(hwnd):
  win32gui.SetForegroundWindow(hwnd)

def main():
  windows = []
  win32gui.EnumWindows(winEnumHandler, None)
  
main()

如果我的使用wxPython模块构建的Python应用程序在前台,并且我想要切换到除我的Python应用程序之外的窗口,但每当在前台有另一个应用程序,我想要切换回我的应用程序的主框架或从其运行该Python应用程序的Windows命令行时,代码都可以正常工作,我会收到以下错误:

Exception in thread Thread-2 (showSwitcher):
Traceback (most recent call last):
  File "C:\Users\asamec\AppData\Local\Programs\Python\Python310-32\lib\threading.py", line 1016, in _bootstrap_inner
    self.run()
  File "C:\Users\asamec\AppData\Local\Programs\Python\Python310-32\lib\threading.py", line 953, in run
    self._target(*self._args, **self._kwargs)
  File "c:\Users\asamec\Dropbox\DIY\Python\WinSwitcher\WinSwitcher\src\WinSwitcher.py", line 207, in showSwitcher
    self.switchToApp(app)
  File "c:\Users\asamec\Dropbox\DIY\Python\WinSwitcher\WinSwitcher\src\WinSwitcher.py", line 165, in switchToApp
    self.switchToWindow(hwnd)
  File "c:\Users\asamec\Dropbox\DIY\Python\WinSwitcher\WinSwitcher\src\WinSwitcher.py", line 172, in switchToWindow
    win32gui.SetForegroundWindow(hwnd)
pywintypes.error: (5, 'SetForegroundWindow', 'Access is denied.')   t

所以问题是:我如何绕过"访问被拒绝"异常,为什么在只从另一个窗口切换到在后台运行的我的Python应用程序窗口的情况下抛出它,而不是在从前台的应用程序窗口切换到其他窗口的情况下抛出?

更新

根据为这篇文章提供的第一个版本,似乎由于Windows系统的限制,使用win32gui模块及其API无法进行所需的窗口切换.那么问题是:有没有其他方法可以切换到特定的窗口,即使在Python应用程序不在前台运行的情况下?我之所以问这个问题,是因为如果进程ID已知,则实际上可以使用pywinauto Python模块进行这种切换,代码如下:

from pywinauto import Application

app = Application().connect(process=pid)
app.top_window().set_focus()

我不想使用上述代码的最初原因是我认为它执行得很慢,然而,我发现性能问题出在代码的其他部分.因此,我将这个问题的第一个答案标记为可接受的问题,因为它也回答了我的后续问题:为什么pywinAuto方法不受Windows系统限制的影响?

被接受的ANSER提供了一种解决方案,该解决方案不需要所需应用的PID和用于切换到该应用的相关PYWINAUTO方法.相反,它提供了一种切换到所需窗口的解决方案,而不是通过窗口的hwnd应用程序,这实际上是问题的初衷.答案还解释说,绕过Windows系统窗口切换限制的解决方法在于使用在运行win32gui.SetForegroundWindow(hwnd)方法之前将鼠标指针移出屏幕的trap .

推荐答案

这是操作系统的限制.微软的documentation详细解释了它何时阻止前台窗口设置为SetForegroundWindow:

系统限制哪些进程可以设置前台窗口.只有在满足以下条件之一时,进程才能设置前台窗口:

  • 该进程是前台进程.
  • 该进程由前台进程启动.
  • 该进程收到最后一个输入事件.
  • 没有前台进程.
  • 正在调试该进程.
  • 前台进程不是现代应用程序或开始屏幕.
  • 前景未锁定(请参见LockSetForegoundWindow).
  • 前台锁定超时已到期(请参阅系统参数信息中的SPI_GETFOREGROundLOCKTIMEOUT).
  • 没有处于活动状态的菜单.

当用户正在使用另一个窗口时,应用程序不能强制将一个窗口转到前台.相反,Windows会闪烁窗口的任务栏按钮来通知用户. 可以设置前台窗口的进程可以使另一个进程通过调用AllowSetForegoundWindow函数来设置前台窗口.由dwProcessId指定的进程将无法在用户下次生成输入时设置前台窗口,除非输入指向该进程,或者进程下次调用AllowSetForegoundWindow时(除非指定了该进程).

Additional Details

当呼叫SetForegroundWindow时,pywinauto受到相同的限制.然而,他们通过将鼠标位置设置到屏幕外的某个点来巧妙地绕过这个问题.则可以再次成功地调用SetForegroundWindow.既然pywinauto是开源的,你也可以看看相应的code snippet.

Test

当写字板实例和任务管理器窗口同时打开时,将执行以下程序.这对于 Select 第二个窗口很有效,因为鼠标移出了屏幕.如果将鼠标移动注释掉,则会出现Access is denied.错误.

当然,这需要pywinauto import mouse个,但它允许以这种方式轻松地演示功能.

import win32gui
import win32con
from pywinauto import mouse
from time import sleep


def foreground(window_title):
    hwnd = win32gui.FindWindow(None, window_title)
    win32gui.ShowWindow(hwnd, win32con.SW_SHOW)
    win32gui.SetForegroundWindow(hwnd)


if __name__ == "__main__":
    foreground("Document - Wordpad")
    sleep(1)
    #if you comment the next line you will get a 'Access is denied.' error
    mouse.move(coords=(-10000, 500))
    foreground("Task Manager")

Python相关问答推荐

使用FASTCGI在IIS上运行Django频道

仿制药的类型铸造

为什么tkinter框架没有被隐藏?

将jit与numpy linSpace函数一起使用时出错

通过Selenium从页面获取所有H2元素

Excel图表-使用openpyxl更改水平轴与Y轴相交的位置(Python)

如何在python xsModel库中定义一个可选[December]字段,以产生受约束的SON模式

如果条件不满足,我如何获得掩码的第一个索引并获得None?

与命令行相比,相同的Python代码在Companyter Notebook中运行速度慢20倍

可以bcrypts AES—256 GCM加密损坏ZIP文件吗?

Discord.py -

Python类型提示:对于一个可以迭代的变量,我应该使用什么?

为用户输入的整数查找根/幂整数对的Python练习

获取git修订版中每个文件的最后修改时间的最有效方法是什么?

Python如何导入类的实例

比较两个有条件的数据帧并删除所有不合格的数据帧

使用xlsxWriter在EXCEL中为数据帧的各行上色

Pandas查找给定时间戳之前的最后一个值

保存由PYTHON在EXCEL中所做更改的问题

从`end_date`回溯,如何计算以极为单位的滚动统计量?