问题描述
我有一个Python脚本,可以创建应用程序的实例并显示应用程序的窗口。
我正在尝试激活/聚焦窗口,以便将其置于前景/顶部并具有键盘输入焦点。
下面的代码通常可以正常工作,但是当在执行代码之前打开任务管理器的窗口并对其进行聚焦时,应用程序的窗口将显示在任务管理器下方,任务管理器将保持键盘输入焦点。
代码中的注释是我试图绕过也不起作用的特定问题的尝试。仅当将SwitchToThisWindow
与{{1}一起使用False
或SetWindowPos
与HWND_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,SWP_NOMOVE | SWP_NOSIZE)
# ctypes.windll.user32.SetWindowPos(window_handle,HWND_TOP,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,False)
我也尝试使用功能AllowSetForegroundWindow
,LockSetForegroundWindow
和SystemParametersInfoW
来将SPI_SETFOREGROUNDLOCKTIMEOUT
设置为0
,但出现错误Access denied.
来自ctypes.FormatError()
。
有什么办法可以实现?
解决方法
您需要在清单中将UIAccess设置为true,以支持辅助功能。
以UIAccess权限启动的进程具有以下内容 能力:
- 设置前景窗口。
- 使用SendInput函数驱动任何应用程序窗口。
- 通过使用低级挂钩,原始输入,GetKeyState,GetAsyncKeyState和GetKeyboardInput,将读取输入用于所有完整性级别。
- 设置日记帐挂钩。
- 使用
AttachThreadInput
将线程附加到更高完整性的输入队列。
首先,在清单中设置uiAccess=true
。
然后,签名代码。
最后,将其放在文件系统上的安全位置:
- \ Program Files \包括子目录
- \ Windows \ system32 \
- \ Program Files(x86)\,包括64位版本的 Windows
您可以参考this document和this answer。
更新:
要将UIAccess设置为python脚本:
- 安装PyInstaller:
pip install pyinstaller
。 - 使用Pyinstaller从Python脚本生成可执行文件。
这是我的测试示例,它设置了一个5秒的计时器以将窗口移至顶部。
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,500,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,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
- 创建证书并签名应用程序,进行抽样(不要忘记将证书安装到受信任的根证书颁发机构):https://stackoverflow.com/a/63193360/10611792
- 将其放置在文件系统上的安全位置,例如,我将
dist\\hello
放入了C:\\Program Files
结果: