Search code examples
pythonwindowspywin32

pywin32 SetFocus resulting in 'Access is denied' error


I'm creating a script that will send mouse inputs to a specific window. The only way I have found to do this is by setting focus to the window. If there is another way please explain it. Anyways, this is the code that is not working.

from win32gui import *
us_ip = input('Version of Minecraft: ')

minecraft_handle = FindWindow(None, f'Minecraft {us_ip}')
SetFocus(minecraft_handle)

I can retrieve the HWND but when I run the program I get this error.

Traceback (most recent call last):
  File "c:\Users\Jacob Daniels\Desktop\python\autoclicker\windows api experimentation\py.py", line 5, in <module>
    SetFocus(minecraft_handle)
pywintypes.error: (5, 'SetFocus', 'Access is denied.')

Any help will be appreciated. Documentation for library: http://timgolden.me.uk/pywin32-docs/win32_modules.html edit: posted wrong error message


Solution

  • According to [MS.Docs]: SetFocus function (emphasis is mine):

    Sets the keyboard focus to the specified window. The window must be attached to the calling thread's message queue.

    ...

    By using the AttachThreadInput function, a thread can attach its input processing to another thread. This allows a thread to call SetFocus to set the keyboard focus to a window attached to another thread's message queue.

    Obviously the "remote" window is not attached to the Python script (main) thread, so that needs to be taken care of (with a bit of extra work).

    code00.py:

    #!/usr/bin/env python
    
    import sys
    import win32gui as wgui
    import win32process as wproc
    import win32api as wapi
    
    
    def main(*argv):
        if not argv:
            window_name = input("Enter window name: ")
        else:
            window_name = argv[0]
    
        handle = wgui.FindWindow(None, window_name)
        print("Window `{0:s}` handle: 0x{1:016X}".format(window_name, handle))
        if not handle:
            print("Invalid window handle")
            return
        remote_thread, _ = wproc.GetWindowThreadProcessId(handle)
        wproc.AttachThreadInput(wapi.GetCurrentThreadId(), remote_thread, True)
        prev_handle = wgui.SetFocus(handle)
    
    
    if __name__ == "__main__":
        print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
        main(*sys.argv[1:])
        print("\nDone.")
    

    Output:

    [cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q062649124]> "e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe" code00.py "Untitled - Notepad"
    Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 64bit on win32
    
    Window `Untitled - Notepad` handle: 0x0000000004A520AA
    
    Done.
    

    Needless to say that the Notepad window popped up in front of Cmd terminal.
    Note that for some windows, it might not work.