Reputation: 2440
I have a Python script which creates an instance of an application and shows the application's window.
I'm trying to activate/focus the window so that it brings it to the foreground/top and has the keyboard input focus.
The code below usually works, but when the task manager's window is opened and focused before the code is executed, the application's window appears below the task manager and the task manager keeps the keyboard input focus.
The comments in the code are my attempts to circumvent the specific problem which didn't worked either. Only when using SwitchToThisWindow
with False
or SetWindowPos
with HWND_TOPMOST
(which sets the window as top most), does the window appear on top of the task manager's window, but the task manager still keeps the keyboard input focus.
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)
I have also attempted using the functions AllowSetForegroundWindow
, LockSetForegroundWindow
and SystemParametersInfoW
to set SPI_SETFOREGROUNDLOCKTIMEOUT
to 0
but I get the error Access denied.
from ctypes.FormatError()
.
Is there any way to accomplish it?
Upvotes: 2
Views: 2724
Reputation: 7170
You need to set UIAccess to true in the manifest to support accessibility features.
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.
First, set the uiAccess=true
in the manifest.
Then, sign the code.
Finally, put it in a secure location on the file system:
You could refer to this document and this answer.
UPDATE:
To set UIAccess to the python script:
pip install pyinstaller
.Here is my testing sample, it sets a 5sec timer to bring the window to top.
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()
use --manifest <FILE or XML>
option or use pyinstaller --uac-uiaccess hello.pyw
directly, then the exe file is located in dist\\hello
dist\\hello
in the C:\\Program Files
Result:
Upvotes: 3