J'ai un système avec deux claviers HID (en fait, l'un est un scanner de codes à barres.)Comment émuler WM_KEYDOWN, WM_KEY * à partir d'un gestionnaire WM_INPUT enregistré avec RIDEV_NOLEGACY?
J'enregistré pour l'entrée brute avec RIDEV_NOLEGACY pour bloquer le système de création WM_KEY * messages pour le scanner de codes à barres, qui fastidieusement bloque également les messages de l'autre clavier.
Mon but est de conserver les messages WM_ * pour tout périphérique keybaord qui n'est pas le lecteur de codes-barres.
Fondamentalement, je dois soit:
- Créer le WM_ * moi-même les messages et les envoyer à mon hwnd du wndproc qui a reçu le wm_input
ou
- Prédisez les messages WM_ * générés par le système et ignorez-les s'ils proviennent du lecteur de codes-barres.
J'ai créé une mise en œuvre de travail 2, qui fonctionne très bien sur XP, mais ne parvient pas à bloquer quoi que ce soit sur Windows 7. (En fait, sur win7 il semble que je suis seulement reciving WM_INPUTs même sans le drapeau RIDEV_NOLEGACY)
J'essaie maintenant la méthode 1, qui est sans doute "plus correcte", mais je ne peux pas trouver un moyen de le faire complètement correctement.
Mon environnement est Python 2.6 utilisant PyQt. J'envoie des messages directement à une fenêtre créée par PyQt, et je l'ai accroché dans wndproc avec un filtre d'événement win32.
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)
Ce code fonctionne, mais certaines choses (comme maintenant la touche Maj enfoncée, etc.) échouent. Également très étrange est que lorsque vous utilisez SendMessage, les lettres que je tape sont en majuscules, mais le passage à PostMessage les rend en minuscules. Je peux probablement résoudre cela via Get/SetKeyState, mais j'espérais que quelqu'un pourrait me donner des réponses.
De plus, je poste ces messages dans la file d'attente de PyQt, mais l'application ne parvient pas à les traiter jusqu'à ce qu'un événement réel soit généré par sytem. C'est-à-dire, si je tape une phrase dans une zone de texte, rien n'apparaît jusqu'à ce que je déplace ensuite ma souris sur la fenêtre. Les messages semblent placés en file d'attente jusqu'à ce qu'un événement réel se produise. Aucune suggestion?
Précision:
Ceci est une fenêtre dans mon propre processus, créé par PyQt. J'ai obtenu c'est hwnd, et accroché la notification d'entrée brute à elle. Dans la procédure de fenêtre pour WM_INPUT sur ce hwnd, je veux envoyer un message à mon propre hwnd pour dupliquer les messages «hérités» WM_KEY * que j'ai précédemment désactivés pour les filtrer. Encore une fois, tout cela se passe dans mon propre processus, dans mon propre fil.
Mise à jour:
Décalage détection de l'état ne fonctionne tout simplement pas. Peu importe quoi, je reçois toutes les clés majuscules. Aucun conseil?
Je n'a pas pu résoudre ce dans Win32 pur, et je l'ai obtenu seulement une solution demi que j'utilise PyQt. Au cas où quelqu'un se intéresse bien, voici le code que je utilise pour cette partie:
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
Ah, mais je suis l'application de réception. Je vais mettre à jour le résumé pour être plus clair. –
Hmm, ne l'ai pas vu venir. Pourquoi ne manipulez-vous pas le message directement? SetKeyState() pour contrôler les touches de modification devrait certainement fonctionner. –
J'utilise un framework UI (spécifiquement PyQt). Je voudrais que Win32 le fasse, de sorte que PyQt le fasse, surtout pour ne pas avoir à me fier aux spécificités de Qt, et en partie pour que cette partie du code (le lecteur de code à barres rawinput) puisse rester un plugin , et ne pas avoir une connaissance intime de l'interface utilisateur. Fondamentalement, les seules exigences que je veux que la partie de code à barres ait est «appelez-moi dans votre wndproc, et je vais gérer le reste, et vous appelez s'ils scannent quelque chose». C'est la partie que j'écris maintenant. –