user7393973
user7393973

Reputation: 2440

How to focus an application's window to the foreground with keyboard focus when the task manager's window is focused?

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

Answers (1)

Drake Wu
Drake Wu

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:

  • \Program Files\ including subdirectories
  • \Windows\system32\
  • \Program Files (x86)\ including subdirectories for 64-bit versions of Windows

You could refer to this document and this answer.

UPDATE:

To set UIAccess to the python script:

  1. Install PyInstaller: pip install pyinstaller.
  2. Generate executable from Python Script using 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

  1. Create the certificate and sign the application, sample(and don't forget to install the certificate to the Trusted Root Certication Authorities): https://stackoverflow.com/a/63193360/10611792
  2. Place it in a secure location on the file system, for example I put the dist\\hello in the C:\\Program Files

Result:

enter image description here

Upvotes: 3

Related Questions