@Perforator

Как осуществить передачу комбинации клавиш (CTRL+A, etc.) неактивному окну?

Возникла необходимость передать комбинацию клавиш в соседнее, не активное и не свёрнутое окно.

В случае передачи единичных нажатий клавиш проблем никаких не возникало, однако, с комбинациями клавиш завис.

Вычитал, что простого SendMessage/PostMessage для передачи комбинации не достаточно.
Нужно ещё и выставить состояние клавиатуры, для чего используют AttachThreadInput и SetKeyboardState.

AttachThreadInput срабатывает (если реально зажать клавишу CTRL в активном [не Notepad] окне - то при отработке скрипта выделение текста в Notepad происходит)

Зато SetKeyboardState либо не работает либо нужно что-то ещё, т.к. вместо выделения текста просто печатается буква "a"

В чём может быть загвоздка ?

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)
  • Вопрос задан
  • 5946 просмотров
Решения вопроса 1
@Perforator Автор вопроса
Нашёл решение:
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)
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы