# Pulsa un botón de un diálogo de Windows mandándole un mensaje (BM_CLICK), sin mover el mouse
# ni necesitar que la ventana esté al frente. De libre uso (eder3232.com).
#
# Uso: python pulsar-boton.py <pid> <ejecutable> "&Continue" ["&Continuar" ...]
#      python pulsar-boton.py 12345 acad.exe "&Close"
#
# - Solo actúa sobre el proceso <pid>, y solo si ese proceso es <ejecutable>: Windows reutiliza los PID,
#   y uno que era tu programa puede ser otro (en la prueba, un PID anotado como AutoCAD ya era Chrome).
# - Usa el texto exacto del botón, con su "&" (el atajo de teclado). Con UI Automation se ve el árbol de controles.
# - Sirve para diálogos clásicos de Win32 (informe de error, recuperación, Guardar como). Con controles WPF
#   o dibujados a mano no hay ventana hija por botón: ahí hace falta UI Automation.
import ctypes, ctypes.wintypes as wt, os, sys

if len(sys.argv) < 4:
    sys.exit(__doc__ if __doc__ else "uso: pulsar-boton.py <pid> <ejecutable> <texto> [<texto>...]")
u32, k32 = ctypes.windll.user32, ctypes.windll.kernel32
pid, exe, textos = int(sys.argv[1]), sys.argv[2].lower(), set(sys.argv[3:])

# Comprobar que el PID es del ejecutable esperado
h = k32.OpenProcess(0x1000, False, pid)  # PROCESS_QUERY_LIMITED_INFORMATION
if not h:
    sys.exit(f"no existe el proceso {pid}")
buf, n = ctypes.create_unicode_buffer(1024), wt.DWORD(1024)
k32.QueryFullProcessImageNameW(h, 0, buf, ctypes.byref(n))
k32.CloseHandle(h)
if os.path.basename(buf.value).lower() != exe:
    sys.exit(f"el proceso {pid} es {os.path.basename(buf.value)}, no {exe}: no se toca")

pulsados = []
PROC = ctypes.WINFUNCTYPE(ctypes.c_bool, wt.HWND, wt.LPARAM)


def hijo(x, _):
    largo = u32.GetWindowTextLengthW(x)
    t = ctypes.create_unicode_buffer(largo + 1)
    u32.GetWindowTextW(x, t, largo + 1)
    if t.value in textos:
        u32.PostMessageW(x, 0x00F5, 0, 0)  # BM_CLICK
        pulsados.append(t.value)
    return True


def ventana(w, _):
    p = wt.DWORD()
    u32.GetWindowThreadProcessId(w, ctypes.byref(p))
    if p.value == pid and u32.IsWindowVisible(w):
        u32.EnumChildWindows(w, PROC(hijo), 0)
    return True


u32.EnumWindows(PROC(ventana), 0)
print("pulsados:", pulsados)
