Python 中单独线程中的按键回显不显示第一个击键

2023-12-28

我会尝试发布一个最小的工作示例,但不幸的是这个问题只需要很多部分,所以我已尽我所能将其剥离。

首先,我使用一个简单的脚本来通过函数调用模拟按键。这是调整自here https://stackoverflow.com/a/13290031/2924421.

import ctypes

SendInput = ctypes.windll.user32.SendInput

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)]

def getKeyCode(unicodeKey):
    k = unicodeKey
    curKeyCode = 0
    if k == "up": curKeyCode = 0x26
    elif k == "down": curKeyCode = 0x28
    elif k == "left": curKeyCode = 0x25
    elif k == "right": curKeyCode = 0x27
    elif k == "home": curKeyCode = 0x24
    elif k == "end": curKeyCode = 0x23
    elif k == "insert": curKeyCode = 0x2D
    elif k == "pgup": curKeyCode = 0x21
    elif k == "pgdn": curKeyCode = 0x22
    elif k == "delete": curKeyCode = 0x2E
    elif k == "\n": curKeyCode = 0x0D

    if curKeyCode == 0:
        return 0, int(unicodeKey.encode("hex"), 16), 0x0004
    else:
        return curKeyCode, 0, 0

def PressKey(unicodeKey):
    key, unikey, uniflag = getKeyCode(unicodeKey)

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

def ReleaseKey(unicodeKey):
    key, unikey, uniflag = getKeyCode(unicodeKey)
    extra = ctypes.c_ulong(0)
    ii_ = Input_I()
    ii_.ki = KeyBdInput( key, unikey, uniflag + 0x0002, 0, ctypes.pointer(extra) )
    x = Input( ctypes.c_ulong(1), ii_ )
    ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))

我将其存储在名为 keyPress.py 的文件中。

使用这个,我想制作一个简单的程序,可以检测用户在 python shell 中输入的内容。我的想法是,我将使用 msvcrt.getch() 来按下按键,然后使用上面的脚本使其看起来仍然被按下(并且在某种意义上“回显”按键”)

这是代码:

import keyPress
import msvcrt        
import threading

def getKey():

    k = msvcrt.getch()

    # Escaped Key: 224 is on the keyboard, 0 is on the numpad
    if int(k.encode("hex"), 16) == 224 or int(k.encode("hex"), 16) == 0:
        k = msvcrt.getch()
        if k == "H": k = "up"
        elif k == "P": k = "down"
        elif k == "K": k = "left"
        elif k == "M": k = "right"
        elif k == "G": k = "home"
        elif k == "O": k = "end"
        elif k == "R": k = "insert"
        elif k == "I": k = "pgup"
        elif k == "Q": k = "pgdn"
        elif k == "S": k = "delete"

    # Fix weird linebreak
    if k == "\r":
        k = "\n"

    return k


def actualGetKeys():
    while True:
        k = getKey()
        keyPress.PressKey(k)
        keyPress.ReleaseKey(k)

def getKeys():
    p = threading.Thread(target=actualGetKeys)
    p.daemon = True
    p.start()   

我将其存储在名为 keyGet.py 的文件中。

这一切都工作得很好,除了每当用户按下 Enter 时,第一个键不会显示在屏幕上。控制台仍然知道您输入了它,只是没有显示在那里。像这样的东西:

怎么了?我已经尝试了很多很多事情,但我似乎无法改变这种行为。

我现在能够使其基本上正常工作,因为它可以在脚本运行时异步捕获按键输入,并使用您在命令提示符中键入的每个命令的文本执行(因此您可以将这些存储到数组中) )。我遇到的唯一问题是这样的:

我知道这是因为基本上必须让机器人在输入后重新输入输入,我只是想知道是否有办法做到这一点,以防止机器人在输入时实际显示输入,因此它会起作用正如用户所期望的那样。


这是生成的代码,基本上是由 eryksun 的注释编写的,因为不知何故他知道一切。

这称为 readcmd.py

# Some if this is from http://nullege.com/codes/show/src@e@i@einstein-HEAD@Python25Einstein@[email protected] /cdn-cgi/l/email-protection/380/win32api.GetStdHandle
# and
# http://nullege.com/codes/show/src@v@i@VistA-HEAD@Python@[email protected] /cdn-cgi/l/email-protection/901/win32console.GetStdHandle.PeekConsoleInput

from ctypes import *
import time
import threading

from win32api import STD_INPUT_HANDLE, STD_OUTPUT_HANDLE

from win32console import GetStdHandle, KEY_EVENT, ENABLE_WINDOW_INPUT, ENABLE_MOUSE_INPUT, ENABLE_ECHO_INPUT, ENABLE_LINE_INPUT, ENABLE_PROCESSED_INPUT

import keyPress


class CaptureLines():
    def __init__(self):
        self.stopLock = threading.Lock()

        self.isCapturingInputLines = False

        self.inputLinesHookCallback = CFUNCTYPE(c_int)(self.inputLinesHook)
        self.pyosInputHookPointer = c_void_p.in_dll(pythonapi, "PyOS_InputHook")
        self.originalPyOsInputHookPointerValue = self.pyosInputHookPointer.value

        self.readHandle = GetStdHandle(STD_INPUT_HANDLE)
        self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT)

    def inputLinesHook(self):

        self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT)
        inputChars = self.readHandle.ReadConsole(10000000)
        self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT)

        if inputChars == "\r\n":
            keyPress.KeyPress("\n")
            return 0

        inputChars = inputChars[:-2]

        inputChars += "\n"

        for c in inputChars:
            keyPress.KeyPress(c)

        self.inputCallback(inputChars)

        return 0


    def startCapture(self, inputCallback):
        self.stopLock.acquire()

        try:
            if self.isCapturingInputLines:
                raise Exception("Already capturing keystrokes")

            self.isCapturingInputLines = True
            self.inputCallback = inputCallback

            self.pyosInputHookPointer.value = cast(self.inputLinesHookCallback, c_void_p).value
        except Exception as e:
            self.stopLock.release()
            raise

        self.stopLock.release()

    def stopCapture(self):
        self.stopLock.acquire()

        try:
            if not self.isCapturingInputLines:
                raise Exception("Keystrokes already aren't being captured")

            self.readHandle.SetConsoleMode(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT)

            self.isCapturingInputLines = False
            self.pyosInputHookPointer.value = self.originalPyOsInputHookPointerValue

        except Exception as e:
            self.stopLock.release()
            raise

        self.stopLock.release()

这是 keyPress.py

# Modified from http://stackoverflow.com/a/13615802/2924421

import ctypes
from ctypes import wintypes
import time

user32 = ctypes.WinDLL('user32', use_last_error=True)

INPUT_MOUSE    = 0
INPUT_KEYBOARD = 1
INPUT_HARDWARE = 2

KEYEVENTF_EXTENDEDKEY = 0x0001
KEYEVENTF_KEYUP       = 0x0002
KEYEVENTF_UNICODE     = 0x0004
KEYEVENTF_SCANCODE    = 0x0008

MAPVK_VK_TO_VSC = 0

# C struct definitions
wintypes.ULONG_PTR = wintypes.WPARAM

SendInput = ctypes.windll.user32.SendInput

PUL = ctypes.POINTER(ctypes.c_ulong)

class KEYBDINPUT(ctypes.Structure):
    _fields_ = (("wVk",         wintypes.WORD),
                ("wScan",       wintypes.WORD),
                ("dwFlags",     wintypes.DWORD),
                ("time",        wintypes.DWORD),
                ("dwExtraInfo", wintypes.ULONG_PTR))

class MOUSEINPUT(ctypes.Structure):
    _fields_ = (("dx",          wintypes.LONG),
                ("dy",          wintypes.LONG),
                ("mouseData",   wintypes.DWORD),
                ("dwFlags",     wintypes.DWORD),
                ("time",        wintypes.DWORD),
                ("dwExtraInfo", wintypes.ULONG_PTR))

class HARDWAREINPUT(ctypes.Structure):
    _fields_ = (("uMsg",    wintypes.DWORD),
                ("wParamL", wintypes.WORD),
                ("wParamH", wintypes.WORD))

class INPUT(ctypes.Structure):
    class _INPUT(ctypes.Union):
        _fields_ = (("ki", KEYBDINPUT),
                    ("mi", MOUSEINPUT),
                    ("hi", HARDWAREINPUT))
    _anonymous_ = ("_input",)
    _fields_ = (("type",   wintypes.DWORD),
                ("_input", _INPUT))

LPINPUT = ctypes.POINTER(INPUT)

def _check_count(result, func, args):
    if result == 0:
        raise ctypes.WinError(ctypes.get_last_error())
    return args

user32.SendInput.errcheck = _check_count
user32.SendInput.argtypes = (wintypes.UINT, # nInputs
                             LPINPUT,       # pInputs
                             ctypes.c_int)  # cbSize

def KeyDown(unicodeKey):
    key, unikey, uniflag = GetKeyCode(unicodeKey)
    x = INPUT( type=INPUT_KEYBOARD, ki= KEYBDINPUT( key, unikey, uniflag, 0))
    user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x))

def KeyUp(unicodeKey):
    key, unikey, uniflag = GetKeyCode(unicodeKey)
    extra = ctypes.c_ulong(0)
    x = INPUT( type=INPUT_KEYBOARD, ki= KEYBDINPUT( key, unikey, uniflag | KEYEVENTF_KEYUP, 0))
    user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x))

def KeyPress(unicodeKey):
    time.sleep(0.0001)
    KeyDown(unicodeKey)
    time.sleep(0.0001)
    KeyUp(unicodeKey)
    time.sleep(0.0001)


def GetKeyCode(unicodeKey):
    k = unicodeKey
    curKeyCode = 0
    if k == "up": curKeyCode = 0x26
    elif k == "down": curKeyCode = 0x28
    elif k == "left": curKeyCode = 0x25
    elif k == "right": curKeyCode = 0x27
    elif k == "home": curKeyCode = 0x24
    elif k == "end": curKeyCode = 0x23
    elif k == "insert": curKeyCode = 0x2D
    elif k == "pgup": curKeyCode = 0x21
    elif k == "pgdn": curKeyCode = 0x22
    elif k == "delete": curKeyCode = 0x2E
    elif k == "\n": curKeyCode = 0x0D

    if curKeyCode == 0:
        return 0, int(unicodeKey.encode("hex"), 16), KEYEVENTF_UNICODE
    else:
        return curKeyCode, 0, 0
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python 中单独线程中的按键回显不显示第一个击键 的相关文章

随机推荐

  • 调整矩形大小并捕捉到固定比例

    我目前的代码可以在用户单击对应于以下区域的多个锚点时根据鼠标移动调整矩形大小 左 上 右 下 左上 右上 左下 右下 因此 用户可以单击并拖动顶部锚点 它将根据鼠标所在的位置调整矩形的顶部坐标 对于所有其他锚点也是如此 之后 我想通过根据操
  • 如何使用 JavaScript(外部域)解析 RSS 提要?

    Question 我需要解析 RSS 提要并在 HTML 页面中显示解析的详细信息 我找到的解决方案 如何使用 JavaScript 解析 RSS 提要 https stackoverflow com questions 10943544
  • 如果浏览器中未启用 JavaScript,则显示消息

    我正在寻找一种解决方案 如果我的网站的访问者没有启用 JavaScript 则向他显示一条信息消息 我在 div 中尝试了一条消息 该消息默认可见 但在启动时立即被 jQuery 函数隐藏 问题是 该消息在短时间内可见 直到它被隐藏 这非常
  • 如何在正则表达式中指定单引号

    这是我的正则表达式 我想在 O Neal Nickel 等字符中包含单引号 这是我的正则表达式 允许字母 空格以及句号 和 连字符 A Za z s A Za z s 还是我问错了你的问题
  • 您可以使用 Sel-Blocks Selenium 插件指定数据文件的“相对”路径吗?

    使用 SelBlocks 插件进行数据驱动开发时 指定数据文件的位置时是否可以指定相对路径 而不是绝对路径 我在一个小团队工作 我们希望能够让我们的测试可移植并最终成为我们生产过程的一部分 我们目前正在使用 Selenium IDE 来编写
  • Mantle自动将0和1转换为BOOL?

    Mantle 是否已将 JSON 中的 int 值 0 和 1 转换为 Objective C BOOL 值 我有一个模型 interface MyModel MTLModel property nonatomic readonly BOO
  • Ruby on Rails 和 Jquery:提交后尝试切换字符

    第一篇文章在这里 所以如果有些事情不对 我如何放入代码 如果我没有包含足够的内容 或者如果我的问题太模糊 我深表歉意 我对 RoR 非常非常陌生 我正从构建一款井字棋游戏开始 我已经将其构建为可以在命令行上运行 现在我正在尝试使其基于网络
  • 如何确保在运行 Jenkins 管道之前更新参数列表?

    Jenkins 管道项目配置为获取其Jenkinsfile来自 Git 存储库 例如 如果我更改参数列表 properties parameters string name FOO description Choose foo to pro
  • Firebase 导致“线程 1:信号 SIGABRT”

    我启动了一个空白的 Xcode 项目 我所做的就是通过 Cocoapods 添加 Firebase 框架并导入 Appdelegate 和 viewcontroller 当我添加FIRApp configure to didFinishLo
  • 在 Android 中通过 Intent 传递信息后将项目添加到 listView

    我正在尝试添加一个项目 首先使用添加按钮 然后转到不同的活动 然后返回原始活动并将其添加到列表视图中 我似乎不能拥有超过一件物品 AddScreen class 我的第一个活动 package com painLogger IMPORTS
  • 如何在C++中读取空字符串?

    考虑 C 中的这段代码片段 string str cin gt gt str 如果我在这个阶段简单地按回车键 指针就会移动到下一行 但会继续等待输入 即使我故意想输入一个空字符串 我的目标是读一个单词 如果我按 Enter 键 它应该被视为
  • 手机是否需要连接到互联网以便网络提供商可以确定位置?

    手机是否需要始终连接到互联网 以便网络提供商可以确定位置 当我在手机上测试我的应用程序时Settings gt Location only Use wireless network已选中 并且我未通过 Wi Fi 连接到互联网 因此无法获取
  • 将 Git 与 TFS 2017 结合使用 - 可在 Visual Studio 中使用,但不能在命令行中使用

    最近 我们的项目使用 Git 切换到 Team Foundation Server 2017 如果我们通过 Web 界面或 Visual Studio 使用它 我们的 TFS 工作得足够好 但如果我们尝试使用第三方工具 如 Sourcetr
  • 在核心数据持久存储中传送静态(只读)数据的正确方法是什么?

    我想发送静态只读数据以在我的核心数据模型中使用 问题是 存在明显不同的持久存储类型 我不知道这些类型的格式是否应该是不透明的 或者我是否应该能够手动构造它们 现在我只有一个 plist 而且非常小 总共可能有 30 个条目 我是否应该在首次
  • 将 pdf 附件添加到 WooCommerce 已完成订单电子邮件通知

    在另一个线程上找到此代码 但无法使其工作 PDF 上传到 wp content child theme 目标是将 pdf 附加到 woocommerce 将发送的已完成订单电子邮件中 不知道customer completed order是
  • 在map.forEach期间map.delete(key)

    通常 当您循环访问列表时 无法安全地从列表中删除项目 这个概念对于 ES6 地图仍然适用吗 我尝试了这个简单的测试 没有例外 var map new Map a 1 b 2 c 3 map forEach value key map gt
  • 捕获 Windows 上的访问违规

    我试图捕获应用程序中所有未处理的异常 以便在发生异常时可以保存日志文件 这是一个使用 Visual Studio 2013 编译的 64 位 Windows 应用程序 用 C 编写 为了进行测试 我使用 VS 生成的默认 C Win32 项
  • rebol解析函数可以创建完全解析css2/css3的规则吗?

    rebol 解析函数的能力有限制吗 它是否能够解析整个 css2 css 3 规范 或者是否会遇到理论上不可能形成某些规则的情况 HostileFork 回答后更新 我的意思是在正则表达式中我认为这是相当不可能的 解析是否更强大 如果是 这
  • Angular 指令中的单向绑定

    官方Angular 文档用于编译 https docs angularjs org api ng service 24compile讨论单向绑定类型 lt 在角社区 I see 通常称为 单向绑定类型 是什么赋予了 这 在我看来 这并不是真
  • Python 中单独线程中的按键回显不显示第一个击键

    我会尝试发布一个最小的工作示例 但不幸的是这个问题只需要很多部分 所以我已尽我所能将其剥离 首先 我使用一个简单的脚本来通过函数调用模拟按键 这是调整自here https stackoverflow com a 13290031 2924