当任务管理器的 window 获得焦点时,如何使用键盘焦点将应用程序的 window 集中到前台?
How to focus an application's window to the foreground with keyboard focus when the task manager's window is focused?
我有一个 Python 脚本,它创建一个应用程序的实例并显示应用程序的 window。
我正在尝试 activate/focus window 以便将它带到 foreground/top 并获得键盘输入焦点。
下面的代码通常可以工作,但是当任务管理器的 window 在代码执行之前打开并聚焦时,应用程序的 window 出现在任务管理器下方并且任务管理器保留键盘输入焦点。
代码中的注释是我试图规避特定问题的尝试,但也没有奏效。只有当 SwitchToThisWindow
与 False
或 SetWindowPos
与 HWND_TOPMOST
一起使用时(将 window 设置为最顶部),window 才会出现在顶部任务管理器的 window,但任务管理器仍保持键盘输入焦点。
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)
我也曾尝试使用函数 AllowSetForegroundWindow
、LockSetForegroundWindow
和 SystemParametersInfoW
将 SPI_SETFOREGROUNDLOCKTIMEOUT
设置为 0
但我得到错误 Access denied.
来自 ctypes.FormatError()
.
有什么办法可以实现吗?
您需要在清单中将 UIAccess 设置为 true 以支持辅助功能。
A process that is started with UIAccess rights has the following
abilities:
- Set the foreground window.
- Drive any application window by using the SendInput function.
- Use read input for all integrity levels by using low-level hooks, raw input, GetKeyState, GetAsyncKeyState, and GetKeyboardInput.
- Set journal hooks.
- Use
AttachThreadInput
to attach a thread to a higher integrity input queue.
首先,在清单中设置uiAccess=true
。
然后,签署代码。
最后,将其放在文件系统的安全位置:
- \Program Files\ 包括子目录
- \Windows\system32\
- \Program Files (x86)\ 包括 64 位版本的子目录
Windows
你可以参考this document and this answer.
更新:
将 UIAccess 设置为 python 脚本:
- 安装 PyInstaller:
pip install pyinstaller
.
- 使用 Pyinstaller 从 Python 脚本生成可执行文件。
这是我的测试示例,它设置了一个 5 秒的计时器来使 window 到达顶部。
hello.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
- 创建证书并签署应用程序示例(并且不要忘记将证书安装到 Trusted Root Certication Authorities):
- 放在文件系统的安全位置,比如我把
dist\hello
放在C:\Program Files
结果:
我有一个 Python 脚本,它创建一个应用程序的实例并显示应用程序的 window。
我正在尝试 activate/focus window 以便将它带到 foreground/top 并获得键盘输入焦点。
下面的代码通常可以工作,但是当任务管理器的 window 在代码执行之前打开并聚焦时,应用程序的 window 出现在任务管理器下方并且任务管理器保留键盘输入焦点。
代码中的注释是我试图规避特定问题的尝试,但也没有奏效。只有当 SwitchToThisWindow
与 False
或 SetWindowPos
与 HWND_TOPMOST
一起使用时(将 window 设置为最顶部),window 才会出现在顶部任务管理器的 window,但任务管理器仍保持键盘输入焦点。
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)
我也曾尝试使用函数 AllowSetForegroundWindow
、LockSetForegroundWindow
和 SystemParametersInfoW
将 SPI_SETFOREGROUNDLOCKTIMEOUT
设置为 0
但我得到错误 Access denied.
来自 ctypes.FormatError()
.
有什么办法可以实现吗?
您需要在清单中将 UIAccess 设置为 true 以支持辅助功能。
A process that is started with UIAccess rights has the following abilities:
- Set the foreground window.
- Drive any application window by using the SendInput function.
- Use read input for all integrity levels by using low-level hooks, raw input, GetKeyState, GetAsyncKeyState, and GetKeyboardInput.
- Set journal hooks.
- Use
AttachThreadInput
to attach a thread to a higher integrity input queue.
首先,在清单中设置uiAccess=true
。
然后,签署代码。
最后,将其放在文件系统的安全位置:
- \Program Files\ 包括子目录
- \Windows\system32\
- \Program Files (x86)\ 包括 64 位版本的子目录 Windows
你可以参考this document and this answer.
更新:
将 UIAccess 设置为 python 脚本:
- 安装 PyInstaller:
pip install pyinstaller
. - 使用 Pyinstaller 从 Python 脚本生成可执行文件。
这是我的测试示例,它设置了一个 5 秒的计时器来使 window 到达顶部。
hello.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
- 创建证书并签署应用程序示例(并且不要忘记将证书安装到 Trusted Root Certication Authorities):
- 放在文件系统的安全位置,比如我把
dist\hello
放在C:\Program Files
结果: