【问题标题】:How to focus an application's window to the foreground with keyboard focus when the task manager's window is focused?当任务管理器的窗口聚焦时,如何使用键盘焦点将应用程序的窗口聚焦到前台?
【发布时间】:2020-12-06 10:56:57
【问题描述】:

我有一个 Python 脚本,它创建一个应用程序实例并显示应用程序的窗口。

我正在尝试激活/聚焦窗口,以便将其带到前台/顶部并获得键盘输入焦点。

下面的代码通常可以工作,但是在代码执行之前打开任务管理器的窗口并获得焦点时,应用程序的窗口出现在任务管理器的下方,并且任务管理器保持键盘输入焦点。

代码中的 cmets 是我试图规避特定问题的尝试,但也没有奏效。只有当SwitchToThisWindowFalseSetWindowPosHWND_TOPMOST(将窗口设置为最顶部)一起使用时,窗口才会出现在任务管理器窗口的顶部,但任务管理器仍然保持键盘输入焦点.

def bring_window_to_top(window_handle):
  import ctypes
  # import win32com.client
  # from win32con import HWND_TOP, HWND_TOPMOST, SWP_NOMOVE, SWP_NOSIZE

  current_thread_id = ctypes.windll.kernel32.GetCurrentThreadId()
  foreground_window_handle = ctypes.windll.user32.GetForegroundWindow()
  foreground_thread_id = ctypes.windll.user32.GetWindowThreadProcessId(foreground_window_handle, None)
  ctypes.windll.user32.AttachThreadInput(current_thread_id, foreground_thread_id, True)
  ctypes.windll.user32.BringWindowToTop(window_handle)
  # ctypes.windll.user32.SwitchToThisWindow(window_handle, True)
  # ctypes.windll.user32.SwitchToThisWindow(window_handle, False)
  # ctypes.windll.user32.SetWindowPos(window_handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE)
  # ctypes.windll.user32.SetWindowPos(window_handle, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE)
  # wscript_shell = win32com.client.Dispatch('WScript.Shell')
  # wscript_shell.SendKeys('%')
  # ctypes.windll.user32.SetForegroundWindow(window_handle)
  # ctypes.windll.user32.SetFocus(window_handle)
  # ctypes.windll.user32.SetActiveWindow(window_handle)
  # ctypes.windll.user32.AttachThreadInput(current_thread_id, foreground_thread_id, False)

我也尝试使用函数AllowSetForegroundWindowLockSetForegroundWindowSystemParametersInfoWSPI_SETFOREGROUNDLOCKTIMEOUT 设置为0,但我从ctypes.FormatError() 收到错误Access denied.

有什么方法可以实现吗?

【问题讨论】:

标签: python winapi focus foreground


【解决方案1】:

您需要在清单中将 UIAccess 设置为 true 以支持辅助功能。

以 UIAccess 权限启动的进程具有以下内容 能力:

  • 设置前景窗口。
  • 使用 SendInput 函数驱动任何应用程序窗口。
  • 通过使用低级挂钩、原始输入、GetKeyState、GetAsyncKeyState 和 GetKeyboardInput 对所有完整性级别使用读取输入。
  • 设置日志挂钩。
  • 使用AttachThreadInput 将线程附加到更高完整性的输入队列。

首先,在清单中设置uiAccess=true

然后,签署代码。

最后,将其放在文件系统上的安全位置:

  • \Program Files\ 包括子目录
  • \Windows\system32\
  • \Program Files (x86)\ 包括 64 位版本的子目录 窗户

您可以参考this documentthis answer

更新:

将 UIAccess 设置为 python 脚本:

  1. 安装 PyInstaller:pip install pyinstaller
  2. 使用 Pyinstaller 从 Python 脚本生成可执行文件。

这是我的测试示例,它设置了一个 5 秒的计时器来将窗口置于顶部。

你好.pyw:

import win32api, win32con, win32gui
import ctypes
class MyWindow:
        
    def __init__(self):
        win32gui.InitCommonControls()
        self.hinst = win32api.GetModuleHandle(None)
        className = 'MyWndClass'
        message_map = {
            win32con.WM_DESTROY: self.OnDestroy,
            win32con.WM_TIMER: self.OnTimer,
        }
        wndcls = win32gui.WNDCLASS()
        wndcls.style = win32con.CS_HREDRAW | win32con.CS_VREDRAW
        wndcls.lpfnWndProc = message_map
        wndcls.lpszClassName = className
        win32gui.RegisterClass(wndcls)
        style = win32con.WS_OVERLAPPEDWINDOW
        self.hwnd = win32gui.CreateWindow(
            className,
            'Title',
            style,
            win32con.CW_USEDEFAULT,
            win32con.CW_USEDEFAULT,
            500,
            500,
            0,
            0,
            self.hinst,
            None
        )
        win32gui.UpdateWindow(self.hwnd)
        win32gui.ShowWindow(self.hwnd, win32con.SW_SHOW)
        ctypes.windll.user32.SetTimer(self.hwnd,1,5000,0)
    def OnDestroy(self, hwnd, message, wparam, lparam):
        win32gui.PostQuitMessage(0)
        return True
    def OnTimer(self, hwnd, message, wparam, lparam):
        current_thread_id = ctypes.windll.kernel32.GetCurrentThreadId()
        foreground_window_handle = ctypes.windll.user32.GetForegroundWindow()
        foreground_thread_id = ctypes.windll.user32.GetWindowThreadProcessId(foreground_window_handle, None)
        ctypes.windll.user32.BringWindowToTop(hwnd)
        return True

w = MyWindow()
win32gui.PumpMessages()

使用--manifest <FILE or XML>选项或直接使用pyinstaller --uac-uiaccess hello.pyw,则exe文件位于dist\\hello

  1. 创建证书并签署应用程序示例(不要忘记将证书安装到受信任的根证书颁发机构):https://stackoverflow.com/a/63193360/10611792
  2. 将其放在文件系统上的安全位置,例如我将dist\\hello 放在C:\\Program Files

结果:

【讨论】:

    猜你喜欢
    • 2011-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-03
    • 2010-10-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多