P
P
Perforator2014-03-18 00:19:54
Python
Perforator, 2014-03-18 00:19:54

How to pass a key combination (CTRL+A, etc.) to an inactive window?

There was a need to transfer a key combination to an adjacent, not active and not minimized window.
In the case of transmitting single keystrokes, there were no problems, however, it hung with key combinations.
I read that a simple SendMessage / PostMessage is not enough to send a combination.
You also need to set the state of the keyboard, for which they use AttachThreadInput and SetKeyboardState .
AttachThreadInput works (if you really hold down the CTRL key in the active [ not Notepad ] window, then when the script is executed, text is selected in Notepad)
But SetKeyboardStateeither does not work or something else is needed, tk. instead of highlighting the text, the letter "a" is simply printed.
What could be the catch?

import ctypes, win32con, win32api, win32gui
import time
PBYTE256 = ctypes.c_ubyte * 256
_user32 = ctypes.WinDLL("user32")

GetKeyboardState = _user32.GetKeyboardState
SetKeyboardState = _user32.SetKeyboardState
MapVirtualKeyA = _user32.MapVirtualKeyA
AttachThreadInput = _user32.AttachThreadInput
oldKeyboardState = PBYTE256()
keyboardStateBuffer = PBYTE256()
GetKeyboardState(ctypes.byref(oldKeyboardState))

hwnd=0x000D1494 # hwnd окна Edit процесса notepad
current = win32api.GetCurrentThreadId()

key='A'
key=ord(key)

lparam = win32api.MAKELONG(0,MapVirtualKeyA(key, 0)) | 0x00000001 
# | 0x00000001 прописал лишь потому что при перехвате нажатий вручную lparam отличался от генерируемого именно на этот последний бит, без | 0x00000001 результат работы скрипта абсолютно такой же
lparam_ctrl = win32api.MAKELONG(0,MapVirtualKeyA(win32con.VK_CONTROL, 0)) | 0x00000001

win32gui.SendMessage(hwnd, win32con.WM_ACTIVATE, win32con.WA_ACTIVE, 0)

AttachThreadInput(current, hwnd, True)

GetKeyboardState( ctypes.byref(oldKeyboardState) )
keyboardStateBuffer[win32con.VK_CONTROL] |= 128
SetKeyboardState( ctypes.byref(keyboardStateBuffer) )

time.sleep(0.1) # тестирования ради
win32api.PostMessage(hwnd, win32con.WM_KEYDOWN, win32con.VK_CONTROL, lparam_ctrl)
time.sleep(0.1)
win32api.PostMessage(hwnd, win32con.WM_KEYDOWN, key, lparam)
time.sleep(0.1)
win32api.PostMessage(hwnd, win32con.WM_KEYUP, key, lparam | 0xC0000000)
time.sleep(0.1)
win32api.PostMessage(hwnd, win32con.WM_KEYUP, win32con.VK_CONTROL, lparam_ctrl | 0xC0000000)
time.sleep(0.1)

SetKeyboardState( ctypes.byref(oldKeyboardState) )
time.sleep(0.1)

AttachThreadInput(current, hwnd, False)

Answer the question

In order to leave comments, you need to log in

1 answer(s)
P
Perforator, 2014-03-25
@Perforator

Found a solution:

import ctypes, time, win32con, win32api, win32gui

PBYTE256 = ctypes.c_ubyte * 256
_user32 = ctypes.WinDLL("user32")
GetKeyboardState = _user32.GetKeyboardState
SetKeyboardState = _user32.SetKeyboardState
PostMessage = win32api.PostMessage
SendMessage = win32gui.SendMessage
FindWindow = win32gui.FindWindow
IsWindow = win32gui.IsWindow
GetCurrentThreadId = win32api.GetCurrentThreadId
GetWindowThreadProcessId = _user32.GetWindowThreadProcessId #очень важно брать функцию из dll, т.к. питоновский враппер (win32process.GetWindowThreadProcessId) выдаёт неправильные значения
AttachThreadInput = _user32.AttachThreadInput

MapVirtualKeyA = _user32.MapVirtualKeyA
MapVirtualKeyW = _user32.MapVirtualKeyW

MakeLong = win32api.MAKELONG
w = win32con #так короче запись

def PostKeyEx( hwnd, key, shift, specialkey):
    if IsWindow(hwnd):
        
        ThreadId = GetWindowThreadProcessId(hwnd, None)
        
        lparam = MakeLong(0, MapVirtualKeyA(key, 0))
        msg_down=w.WM_KEYDOWN
        msg_up=w.WM_KEYUP
        
        if specialkey:
            lparam = lparam | 0x1000000
            
        if len(shift) > 0: #Если есть модификаторы - используем PostMessage и AttachThreadInput
            pKeyBuffers = PBYTE256()
            pKeyBuffers_old = PBYTE256()
            
            SendMessage(hwnd, w.WM_ACTIVATE, w.WA_ACTIVE, 0)
            AttachThreadInput(GetCurrentThreadId(), ThreadId, True)
            GetKeyboardState( ctypes.byref(pKeyBuffers_old ))
            
            for modkey in shift:
                if modkey == w.VK_MENU:
                    lparam = lparam | 0x20000000
                    msg_down=w.WM_SYSKEYDOWN
                    msg_up=w.WM_SYSKEYUP
                pKeyBuffers[modkey] |= 128
    
            SetKeyboardState( ctypes.byref(pKeyBuffers) )
            time.sleep(0.01)
            PostMessage( hwnd, msg_down, key, lparam)
            time.sleep(0.01)
            PostMessage( hwnd, msg_up, key, lparam | 0xC0000000)
            time.sleep(0.01)
            SetKeyboardState( ctypes.byref(pKeyBuffers_old) )
            time.sleep(0.01)
            AttachThreadInput(GetCurrentThreadId(), ThreadId, False)
            
        else: #Если нету модификаторов - используем SendMessage
            SendMessage( hwnd, msg_down, key, lparam)
            SendMessage( hwnd, msg_up, key, lparam | 0xC0000000)
    
hwnd=FindWindow("Notepad", None)
PostKeyEx(hwnd,ord('A'),[w.VK_CONTROL],False)

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question