Hướng dẫn python send keypress to application - python gửi nhấn phím đến ứng dụng

Nếu bạn đưa cửa sổ lên nền trước, nó sẽ xử lý tất cả các đầu vào bàn phím và nó sẽ hướng đầu vào bàn phím đến điều khiển có tiêu điểm. Khi cửa sổ không ở phía trước, không có điều khiển nào của nó có tiêu điểm tích cực và các phím được gửi đến cửa sổ sẽ không tự động được gửi đến điều khiển mà bạn muốn nhận đầu vào.

Tôi sử dụng Foxit Reader, nhưng điều tương tự sẽ áp dụng cho Acrobat hoặc các ứng dụng khác. Tôi đã thử điều này:

from time import sleep
import win32gui
import win32con


def callback(handle, param):
    s = win32gui.GetClassName(handle)
    try:
        print(f'Sending key to {handle}, {s}')
        win32gui.SendMessage(handle, win32con.WM_KEYDOWN, win32con.VK_NEXT, 0)
        win32gui.SendMessage(handle, win32con.WM_KEYUP, win32con.VK_NEXT, 0)
        sleep(2)
    except Exception:
        print('Exception sending to {handle}, {s}')


window_id = win32gui.FindWindow(None, "my_multipage_doc.pdf - Foxit Reader")
win32gui.EnumChildWindows(window_id, callback, 0)

Điều đó đã tạo ra rất nhiều đầu ra, một dòng cho mỗi đối tượng trong cửa sổ, nhưng đây là một bit có liên quan:

...
Sending key to 594376, ScrollBar
Sending key to 1904808, ScrollBar
Sending key to 397704, ScrollBar
Sending key to 397598, AfxFrameOrView140su
Sending key to 397580, AfxWnd140su
Sending key to 397734, AfxWnd140su
Sending key to 1971214, AfxWnd140su
Sending key to 856494, FoxitDocWnd
Sending key to 986558, Static
...

Cả khi một VK_NEXT được gửi đến Scrollbar đầu tiên vì khi nó được gửi đến ____10, người xem được cuộn xuống một trang.

Vì vậy, tôi viết lại như thế này:

import win32gui
import win32con


def send_page_down(handle, param):
    if win32gui.GetClassName(handle) == param:
        win32gui.SendMessage(handle, win32con.WM_KEYDOWN, win32con.VK_NEXT, 0)
        win32gui.SendMessage(handle, win32con.WM_KEYUP, win32con.VK_NEXT, 0)


window_id = win32gui.FindWindow(None, "my_multipage_doc.pdf - Foxit Reader")
win32gui.EnumChildWindows(window_id, send_page_down, 'FoxitDocWnd')

Điều đó làm những gì bạn cần.

Thật kỳ lạ, một cái gì đó khác tôi đã thử, không hoạt động:

import win32gui
import win32con

window_id = win32gui.FindWindow(None, "my_multipage_doc.pdf - Foxit Reader")
viewer_id = win32gui.FindWindowEx(window_id, 0, 'FoxitDocWnd', None)
win32gui.SendMessage(viewer_id , win32con.WM_KEYDOWN, win32con.VK_NEXT, 0)
win32gui.SendMessage(viewer_id , win32con.WM_KEYUP, win32con.VK_NEXT, 0)

Nhưng điều đó thất bại khi cố gắng để có được

...
Sending key to 594376, ScrollBar
Sending key to 1904808, ScrollBar
Sending key to 397704, ScrollBar
Sending key to 397598, AfxFrameOrView140su
Sending key to 397580, AfxWnd140su
Sending key to 397734, AfxWnd140su
Sending key to 1971214, AfxWnd140su
Sending key to 856494, FoxitDocWnd
Sending key to 986558, Static
...
1. Vì vậy, mặc dù
...
Sending key to 594376, ScrollBar
Sending key to 1904808, ScrollBar
Sending key to 397704, ScrollBar
Sending key to 397598, AfxFrameOrView140su
Sending key to 397580, AfxWnd140su
Sending key to 397734, AfxWnd140su
Sending key to 1971214, AfxWnd140su
Sending key to 856494, FoxitDocWnd
Sending key to 986558, Static
...
0 hiển thị khi liệt kê tất cả các cửa sổ con, nhưng nó không thể tìm thấy nó một cách rõ ràng. Nếu bạn có thể tìm thấy những gì sai với điều đó, đó sẽ là một giải pháp đẹp hơn.

Nhìn dưới đây cho giải pháp cuối cùng!

Được sao chép từ chủ đề StackoverFlow của tôi, xin lỗi nếu nó quá dài và cụ thể:

Tôi đã làm việc với Python trong môi trường Windows và tôi đã viết một kịch bản để tự động hóa một số tác vụ trong một trò chơi đã biết. Nhiệm vụ liên quan đến việc sử dụng nặng cả đầu vào chuột và bàn phím.

Tuy nhiên, tập lệnh cho biết chỉ có một vấn đề: nó không thể gửi tổ hợp phím đến ứng dụng. Tôi đã thử ít nhất 3 phương pháp khác nhau mà tôi sẽ đăng bên dưới và một số biến thể (cũng đọc một phần mười của các câu hỏi/câu trả lời tương tự, nhưng không có kết quả)

Đầu tiên, sử dụng mô -đun Win32API:

f = 0x46 # VirtualKey Code of the letter "F", see     http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx 

win32api.keybd_event(f,0,0,0) # holds the "F" key down
time.sleep(2) # waits 2 seconds
win32api.keybd_event(f,0,win32con.KEYEVENTF_KEYUP,0) #     releases the key

Không có gì đặc biệt về nó, hoạt động hoàn hảo (A "F" được gõ) trong bất kỳ trình soạn thảo văn bản nào, trình duyệt ... Tuy nhiên, nếu tôi mở một trò chơi như, nói, Counter -Strike, sau đó KeyStroke bị "mất" - như trong, Không có gì xảy ra. Mặt khác, nếu tôi mở bảng điều khiển của Counter-Strike, thì phím kỹ thuật sẽ được đăng ký (như trong Notepad). Được thử nghiệm trong một trò chơi khác, Liên minh huyền thoại, chính xác là hành vi tương tự. Trong trò chơi thực tế, không có tổ hợp phím được phát hiện. Tuy nhiên, nếu tôi mở cuộc trò chuyện (vẫn còn ingame) và chạy lại kịch bản thì nó sẽ được đăng ký bởi trò chuyện.

Trên phương pháp thứ hai:

shell = win32com.client.Dispatch("WScript.Shell")
shell.SendKeys("F")

Chính xác hành vi tương tự như trên. Hoạt động tốt trong mọi thứ trừ trò chơi, và trong đó chỉ hoạt động trong các cuộc trò chuyện.

Phương thức thứ ba (tín dụng thuộc về bất cứ ai đã đăng nó trong một luồng StackOverflow khác), nâng cao hơn (Gọi SendInput ()) với mô -đun CTYPES. Về lý thuyết, trong ba, cái này là gần nhất để mô phỏng một máy ép khóa thực tế, thực tế:

SendInput = ctypes.windll.user32.SendInput

# C struct redefinitions 
PUL = ctypes.POINTER(ctypes.c_ulong)
class KeyBdInput(ctypes.Structure):

... (some classes are defined here)

# Actuals Functions

def PressKey(hexKeyCode):

    extra = ctypes.c_ulong(0)
    ii_ = Input_I()
    ii_.ki = KeyBdInput( hexKeyCode, 0x48, 0, 0, ctypes.pointer(extra) )
    x = Input( ctypes.c_ulong(1), ii_ )
    ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))

def ReleaseKey(hexKeyCode):

    extra = ctypes.c_ulong(0)
    ii_ = Input_I()
    ii_.ki = KeyBdInput( hexKeyCode, 0x48, 0x0002, 0, ctypes.pointer(extra) )
    x = Input( ctypes.c_ulong(1), ii_ )
    ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))


def KeyPress():
    PressKey(0x46) # press F
    time.sleep(.5)
    ReleaseKey(0x46) #release F

... nó cũng không hoạt động. Thật kỳ lạ, nó hiển thị chính xác hành vi giống như ba lần trước: hoạt động trong bất kỳ trình soạn thảo văn bản/ứng dụng đơn giản nào, bị bỏ qua bởi các trò chơi hoặc chỉ được đăng ký trong phần trò chuyện trò chơi.

Nếu tôi đoán tôi sẽ nói những trò chơi này đang nhận được các sự kiện bàn phím của họ theo một cách khác mà tôi không bao gồm bất kỳ phương pháp nào trong số 3 phương pháp này, do đó bỏ qua các phương pháp này.

Tôi đánh giá cao bất kỳ sự giúp đỡ nào. Nếu có thể, với các ví dụ cụ thể về mã hoạt động trong CS, LOL hoặc các trò chơi tương tự để tôi có điểm xuất phát.

Chỉnh sửa: Tôi "bán giải quyết" vấn đề. Cuối cùng, không sử dụng Python để gửi tổ hợp phím mà chỉ sử dụng nó để gọi một tập lệnh Autohotkey thực hiện công việc nặng nhọc cho tôi. Kiểm tra bình luận của tôi dưới đây để biết thêm chi tiết.

EDIT2: Rất nhiều người đã tìm thấy chủ đề này, và nhiều người tò mò về cách tôi giải quyết vấn đề. Vì rất nhiều người đã giúp đỡ, tôi sẽ chia sẻ với bạn một giải pháp mà tôi đã thấy rằng không liên quan đến AHK (Hurray!), Cùng với thông tin của một số phương pháp. Ở đây chúng tôi đi:

Đúng là tôi đã "khắc phục" vấn đề bằng cách sử dụng Autohotkey và nhìn lại nó là một mớ hỗn độn nóng bỏng. Lúc đầu, những gì tôi đã làm là tôi thực sự đã tải xuống AHK và viết một kịch bản cho nó chạy song song với kịch bản Python (phải tìm hiểu những điều cơ bản về AHK, vì tôi chưa bao giờ sử dụng nó trước đây). Đúng vậy, tôi đã phải chạy hai đoạn mã riêng biệt cùng một lúc, được viết bằng các ngôn ngữ khác nhau để gửi một bàn phím đơn giản cho một trò chơi.

Cách nó hoạt động là tập lệnh AHK sẽ phát hiện các nét bàn phím cấp cao mà tập lệnh Python được tạo ra (cùng một đầu vào mà tôi đã giải thích các ứng dụng "bình thường" có thể phát hiện nhưng các trò chơi của tôi không) và tái tạo điều kiện đó ở cấp độ thấp hơn, Đủ để trò chơi phát hiện (mức thấp hơn là trực tiếp là trực tiếp và rất cần thiết để làm cho nó hoạt động). Điều này rõ ràng là rất khó chịu, mã lộn xộn, sử dụng hai ngôn ngữ kịch bản để thực hiện một nhiệm vụ mà một mình Python nên có khả năng làm nhiều hơn.

Vì vậy, thử thách tiếp theo của tôi là hợp nhất sạch sẽ hơn AHK và kịch bản Python gốc, và vì vậy tôi đã làm khi tôi phát hiện ra Pyahk, một mô -đun bao quanh chính AHK và có thể được nhập vào tập lệnh Python gốc như bất kỳ mô -đun nào khác.

Đó là những năm ánh sáng trước giải pháp ban đầu: Tôi chỉ cần chạy kịch bản Python gốc và bản thân kịch bản sẽ xử lý AHK từ đó, nhưng vẫn còn xa hoàn hảo. Một trong những khuyết điểm rõ ràng nhất là bạn dựa vào mô -đun của bên thứ ba rất nhạy cảm với phiên bản Python của bạn, ví dụ như nó chỉ hoạt động trên Python 2.7 (thật tệ vì hầu hết các dự án của tôi đang sử dụng> 3 và kịch bản ngớ ngẩn này cần được cài đặt Python 2 chỉ danh cho no thôi). Một người khác là một mô -đun khác, vì tất cả những điều tốt đẹp của nó, cần phải được nhập và xử lý cho một cái gì đó mà bạn chỉ cảm thấy Python có thể làm, chắc chắn nếu không có những mánh lới quảng cáo này!

Nhưng thời gian là điều cốt yếu, đặc biệt là vì tôi có các vấn đề khác với mã của mình, vì vậy tôi đã thực dụng về nó và nó hoạt động theo cách đó trong một thời gian dài.

Cuối cùng, một ngày nào đó tôi quyết định tìm một giải pháp quyết định, một điều mà ít nhất sẽ cho phép tôi làm việc với các phiên bản Python mới hơn, và vì vậy tôi đã tìm kiếm như trước đây, và vì vậy nó đã xảy ra! Tôi tình cờ thấy một giải pháp thanh lịch hoạt động chỉ với mô -đun CTYPES và tôi đã sử dụng nó kể từ đó!

import ctypes, time
# Bunch of stuff so that the script can send keystrokes to game #

SendInput = ctypes.windll.user32.SendInput

# C struct redefinitions 
PUL = ctypes.POINTER(ctypes.c_ulong)
class KeyBdInput(ctypes.Structure):
    _fields_ = [("wVk", ctypes.c_ushort),
                ("wScan", ctypes.c_ushort),
                ("dwFlags", ctypes.c_ulong),
                ("time", ctypes.c_ulong),
                ("dwExtraInfo", PUL)]

class HardwareInput(ctypes.Structure):
    _fields_ = [("uMsg", ctypes.c_ulong),
                ("wParamL", ctypes.c_short),
                ("wParamH", ctypes.c_ushort)]

class MouseInput(ctypes.Structure):
    _fields_ = [("dx", ctypes.c_long),
                ("dy", ctypes.c_long),
                ("mouseData", ctypes.c_ulong),
                ("dwFlags", ctypes.c_ulong),
                ("time",ctypes.c_ulong),
                ("dwExtraInfo", PUL)]

class Input_I(ctypes.Union):
    _fields_ = [("ki", KeyBdInput),
                 ("mi", MouseInput),
                 ("hi", HardwareInput)]

class Input(ctypes.Structure):
    _fields_ = [("type", ctypes.c_ulong),
                ("ii", Input_I)]

# Actuals Functions

def PressKey(hexKeyCode):
    extra = ctypes.c_ulong(0)
    ii_ = Input_I()
    ii_.ki = KeyBdInput( 0, hexKeyCode, 0x0008, 0, ctypes.pointer(extra) )
    x = Input( ctypes.c_ulong(1), ii_ )
    ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))

def ReleaseKey(hexKeyCode):
    extra = ctypes.c_ulong(0)
    ii_ = Input_I()
    ii_.ki = KeyBdInput( 0, hexKeyCode, 0x0008 | 0x0002, 0, ctypes.pointer(extra) )
    x = Input( ctypes.c_ulong(1), ii_ )
    ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))

def KeyPress():
    time.sleep(3)
    PressKey(0x10) # press Q
    time.sleep(.05)
    ReleaseKey(0x10) #release Q

Mã này sẽ hoạt động tốt, hãy thử nó. Không cần Autohotkey hoặc bất kỳ mô -đun nào khác. Tôi không biết Python của bạn tiến bộ như thế nào nhưng nó không phức tạp, hoạt động như sau:

Khi bạn gọi hàm keypress (), tập lệnh sẽ, sau 3 giây, sẽ gửi "Q" nhấn đến bất cứ điều gì được lấy nét. Tôi đã sử dụng "Q" vì mã ban đầu của tôi sử dụng nó, nhưng bạn có thể tìm thấy danh sách đầy đủ các mã cho bất kỳ khoản phím nào bạn cần ở đây: http://www.flint.jp/misc/?q=dik&lang=en

Tôi mong rằng nó giúp ích được cho bạn :)