pythonpywin32

win32gui keys getting stuck when sending to minecraft


I am using win32gui to try and send key inputs to my minecraft window, to move my character even while the game is minimized / unfocused. It's supposed to send W for 1 second, release it, then do the same thing with A, wait 1 second and repeat. Instead it just presses W, then A and never releases them again. How can I fix this?

here is the code

import time
import win32gui
import win32con

def find_minecraft_window():
    hwnd = None

    def enum_windows_proc(inner_hwnd, lParam):
        nonlocal hwnd
        if "Minecraft" in win32gui.GetWindowText(inner_hwnd):
            hwnd = inner_hwnd
            return False  
        return True  

    try:
        win32gui.EnumWindows(enum_windows_proc, None)
    except Exception as e:
        print(f"Exception during window enumeration: {e}")
    return hwnd

def is_window_valid(hwnd):
    return win32gui.IsWindow(hwnd)

def hold_key(hwnd, key, duration):
    if not is_window_valid(hwnd):
        raise ValueError("Invalid window handle")

    try:
        win32gui.SendMessage(hwnd, win32con.WM_KEYDOWN, key, 0)
        time.sleep(duration)
        win32gui.SendMessage(hwnd, win32con.WM_KEYUP, key, 0)
        time.sleep(0.5)  # Small delay to ensure the game registers the key release
    except Exception as e:
        print(f"Exception during key press: {e}")

def main():
    print("Starting Minecraft key press script...")
    hwnd = find_minecraft_window()
    if hwnd is None:
        print("Minecraft window not found!")
        return

    print(f"Minecraft window found: {hwnd}")

    try:
        while True:
            # Validate window handle before each key press
            if not is_window_valid(hwnd):
                print("Window handle became invalid, trying to find Minecraft window again.")
                hwnd = find_minecraft_window()
                if hwnd is None:
                    print("Minecraft window not found!")
                    return

            # hold 'W' for 2 seconds, then 'A' for 1 second
            hold_key(hwnd, ord('1'), 1)
            hold_key(hwnd, ord('2'), 1)

            # Restart loop
            time.sleep(1)  

    except KeyboardInterrupt:
        print("Script stopped by user.")

if __name__ == "__main__":
    main()

Solution

  • Following the documentation for WM_KEYUP, the lParam field requires several bits to be set:

    bits meaning
    0-15 The repeat count ... The repeat count is always 1 for a WM_KEYUP message.
    ... ...
    30 The previous key state. The value is always 1 for a WM_KEYUP message.
    31 The transition state. The value is always 1 for a WM_KEYUP message.

    I cannot try this out right away, but something like this should set bits 1, 30, and 31 for lParam:

    win32gui.SendMessage(hwnd, win32con.WM_KEYUP, key, (3<<30) + 1)