如何从使用 RIDEV_NOLEGACY 注册的 WM_INPUT 处理程序模拟 WM_KEYDOWN、WM_KEY*?

问题描述 投票:0回答:1

我的系统有两个 HID 键盘(实际上,一个是条形码扫描仪。)

我使用 RIDEV_NOLEGACY 注册原始输入,以阻止系统为条形码扫描仪创建 WM_KEY* 消息,这也会阻止来自其他键盘的消息。

我的目标是保留除条形码扫描仪之外的任何键盘设备的 WM_* 消息。

基本上,我需要:

  1. 自己创建 WM_* 消息,并将它们从接收 wm_input 的 wndproc 发布到我的 hwnd

  1. 预测系统将生成的 WM_* 消息,如果它们来自条形码扫描仪,则忽略它们。

我创建了 2 的工作实现,它在 XP 上运行良好,但无法阻止 Windows 7 上的任何内容。(事实上,在 win7 上,即使没有 RIDEV_NOLEGACY 标志,我似乎也只接收 WM_INPUT)

我现在正在尝试方法1,这可以说是“更正确”,但我似乎找不到一种方法来完全正确地做到这一点。

我的环境是使用 PyQt 的 Python 2.6。我直接将消息发送到 PyQt 创建的窗口,并且我已使用 win32 事件过滤器连接到它的 wndproc。


class wm_lparam(Structure):
     _fields_ = [("repeat_count", c_short),
                 ("scancode", c_byte),
                 ("extended_key", c_int, 1),
                 ("reserved", c_int, 4),
                 ("context_code", c_int, 1),
                 ("prev_state", c_int, 1),
                 ("transition_state", c_int, 1),
                ]
assert sizeof(wm_lparam) == 8, sizeof(wm_lparam)
WM_KEYDOWN = 0x0100
WM_KEYUP = 0x0101

WM_SYSKEYDOWN = 0x0104
WM_SYSKEYUP = 0x0105

ALL_WM_KEYDOWN = (WM_KEYDOWN, WM_SYSKEYDOWN)
ALL_WM_KEYUP = (WM_KEYUP, WM_SYSKEYUP)
VK_SHIFT = 0x10
VK_LSHIFT = 0xA0
VK_RSHIFT = 0xA1

    #These values are filled in by my WM_INPUT handler and the RAWINPUT struct
@staticmethod
def _synthesize_wm_legacy(hwnd, wm, vk, scancode, modifider_keys=None):
        kbState_old = (c_byte*255)()
        kbState = (c_byte*255)()

        def keydown(vk):
            return bool(user32.GetAsyncKeyState(vk) & 0x8000)

        kbState[VK_SHIFT] = 0x80 if keydown(VK_SHIFT) else 0
        kbState[VK_LSHIFT] = 0x80 if keydown(VK_SHIFT) else 0

        user32.GetKeyboardState(kbState_old)
        user32.SetKeyboardState(kbState)
        lParam = wm_lparam()
        lp = c_uint.from_address(ctypes.addressof(lParam))
        lParam.repeat_count = 0
        lParam.scancode = scancode
        lParam.extended_key = 0

        if wm in ALL_WM_KEYDOWN:
            lParam.context_code = 0
            lParam.prev_state = 0
            lParam.transition_state = 0
        if wm in ALL_WM_KEYUP:
            lParam.repeat_count = 0
            lParam.context_code = 0
            lParam.prev_state = 1
            lParam.transition_state = 1
        lp = lp.value
        if wm in ALL_WM_KEYUP: #Seems ctypes doesn't like my struct definition.
            lp |= 1 << 30
            lp |= 1 << 31
        log.debug("Posting %s %s %s %08x\n%s"%(hwnd, wm_str(wm), vk, lp, lParam.dump_s()))
        user32.SendMessageA(hwnd, wm, vk, lp)
        user32.SetKeyboardState(kbState_old)

此代码有效,但某些操作(例如按住 Shift 键等)失败。另外很奇怪的是,当使用SendMessage时,我输入的字母是大写的,但是切换到PostMessage时,它们会变成小写。我可能可以通过 Get/SetKeyState 解决这个问题,但我希望有人能给我一些答案。

此外,我将这些消息发布回 PyQt 的队列,但应用程序无法处理它们,直到系统生成真正的事件。也就是说,如果我在文本框中输入一个句子,则在我将鼠标移到窗口上之前不会显示任何内容。这些消息似乎排队等待真正的事件发生。有什么建议吗?


澄清:

这是我自己进程中的一个窗口,由 PyQt 创建。我已经得到了它的 hwnd,并将原始输入通知连接到它。在此 hwnd 上的 WM_INPUT 窗口过程中,我想将消息发送到我自己的 hwnd 以复制我之前禁用的“旧版”WM_KEY* 消息以过滤它们。再说一遍,这一切都发生在我自己的进程中,在我自己的线程中。


更新:

换档状态检测根本不起作用。无论如何,我都会得到所有大写键。有什么建议吗?


我无法在纯 Win32 中解决这个问题,而且自从我使用 PyQt 以来我只得到了一半的解决方案。如果有人感兴趣,这是我用于该部分的代码:

class BarcodeQtEventFiler(QtCore.QObject):
    def __init__(self, parent, *args):
        self.log = logging.getLogger(__name__ + '.keyevent')
        self.app = parent
        self.input_to_surpress = list()
        super(BarcodeQtEventFiler, self).__init__(parent, *args)

    def ignoreKey(self, which):
        """On WM_INPUT from the device, call this with the reported VKey"""
        self.input_to_surpress.append(which)

    def eventFilter(self, object, event):
        if event.type() == QtCore.QEvent.KeyPress:
            if self.input_to_surpress:
                if event.nativeVirtualKey() in self.input_to_surpress:
                    z = None 
                    #This loop eats the suppression buffer until the VK pressed is found. Fixes Dupes for WM key up/down, etc.
                    while z != event.nativeVirtualKey():
                        z = self.input_to_surpress.pop(0)
                    self.log.debug("Ate key press %s (%s)", event.key(), event.text())
                    return True
                else:
                    self.log.debug("Future surpressed input: %s", self.input_to_surpress)
            self.log.debug("Allowing key press %s (%s)", event.key(), event.text())
        return False
winapi keyboard pyqt barcode-scanner raw-input
1个回答
2
投票

此问题无法按原样修复,您无法控制键盘状态。 接收应用程序将使用

GetKeyState()
检查 Shift、Ctrl 或 Alt 键是否按下。
SetKeyState()
不起作用,它只会更改进程的键盘状态,而不更改获取消息的进程。

请使用

SendInput()
代替。 目标进程中的窗口必须具有焦点。

© www.soinside.com 2019 - 2024. All rights reserved.