我按照此處的步驟嘗試從設備讀取一些輸入。我已經嘗試了幾個小時來弄清楚為什么GetMessage
不回傳任何東西。最初我試圖從某個設備上讀取,但看到那不起作用,我只想嘗試讀取鍵盤或滑鼠輸入。但是,我沒有運氣這樣做。
編輯:更多資訊。我在 Windows 10 上。我在 cmder 中運行代碼(不確定這是否有任何區別)python main.py
。沒有錯誤訊息,輸出是Successfully registered input device!
在程式等待從GetMessage
.
這是運行代碼:
主要.py:
from ctypes import windll, sizeof, WinDLL, pointer, c_uint, create_string_buffer, POINTER
from ctypes.wintypes import *
from structures import *
from constants import * # I put a comment specifying the value for each variable used from here
k32 = WinDLL('kernel32')
GetRawInputDeviceInfo = windll.user32.GetRawInputDeviceInfoA
GetRawInputDeviceInfo.argtypes = HANDLE, UINT, LPVOID, PUINT
RegisterRawInputDevices = windll.user32.RegisterRawInputDevices
RegisterRawInputDevices.argtypes = (RawInputDevice * 7), UINT, UINT
GetMessage = windll.user32.GetMessageA
GetMessage.argtypes = POINTER(Message), HWND, UINT, UINT
def print_error(code=None):
print(f"Error code {k32.GetLastError() if code is None else code}")
def register_devices(hwnd_target=None):
# Here I added all usages just to try and get any kind of response from GetMessage
page = 0x01
# DW_FLAGS is 0
devices = (RawInputDevice * 7)(
RawInputDevice(page, 0x01, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x02, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x04, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x05, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x06, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x07, DW_FLAGS, hwnd_target),
RawInputDevice(page, 0x08, DW_FLAGS, hwnd_target),
)
if not RegisterRawInputDevices(devices, len(devices), sizeof(devices[0])):
print_error()
else:
print("Successfully registered input device!")
def get_message(h_wnd=None):
msg = pointer(Message())
# WM_INPUT is 0
return_value = GetMessage(msg, h_wnd, WM_INPUT, WM_INPUT)
if return_value == -1:
print_error()
elif return_value == 0:
print("WM_QUIT message received.")
else:
print("Successfully got message!")
return msg
register_devices()
print(get_message().contents.message)
結構.py:
from ctypes import Structure
from ctypes.wintypes import *
class RawInputDevice(Structure):
_fields_ = [
("usUsagePage", USHORT),
("usUsage", USHORT),
("dwFlags", DWORD),
("hwndTarget", HWND),
]
class Message(Structure):
_fields_ = [
("hwnd", HWND),
("message", UINT),
("wParam", WPARAM),
("lParam", LPARAM),
("time", DWORD),
("pt", POINT),
("lPrivate", DWORD)
]
如果有人幫助我找出問題所在,我將不勝感激,或者如果有人能指出從 Windows 上的 HID 設備讀取輸入的替代方法,我也會很好。
uj5u.com熱心網友回復:
我將從(主要)資源開始:
[MS.Docs]:使用原始輸入
[SO]:簡單 main() 中的 getrawinputdata
[SO]:是否可以在沒有視窗的情況下使用 Windows Raw Input API(即來自控制臺應用程式)?
[CodeProject]:使用 RAWINPUT 的最小鍵盤記錄器
[Python.Docs]: ctypes - Python 的外來函式庫
[GitHub]:mhammond/pywin32 - pywin32
我準備了一個例子。
ctypes_wrappers.py:
import ctypes as ct
import ctypes.wintypes as wt
HCURSOR = ct.c_void_p
LRESULT = ct.c_ssize_t
wndproc_args = (wt.HWND, wt.UINT, wt.WPARAM, wt.LPARAM)
WNDPROC = ct.CFUNCTYPE(LRESULT, *wndproc_args)
kernel32 = ct.WinDLL("Kernel32")
user32 = ct.WinDLL("User32")
def structure_to_string_method(self):
ret = [f"{self.__class__.__name__} (size: {ct.sizeof(self.__class__)}) instance at 0x{id(self):016X}:"]
for fn, _ in self._fields_:
ret.append(f" {fn}: {getattr(self, fn)}")
return "\n".join(ret) "\n"
union_to_string_method = structure_to_string_method
class Struct(ct.Structure):
to_string = structure_to_string_method
class Uni(ct.Union):
to_string = union_to_string_method
class WNDCLASSEXW(Struct):
_fields_ = (
("cbSize", wt.UINT),
("style", wt.UINT),
#("lpfnWndProc", ct.c_void_p),
("lpfnWndProc", WNDPROC),
("cbClsExtra", ct.c_int),
("cbWndExtra", ct.c_int),
("hInstance", wt.HINSTANCE),
("hIcon", wt.HICON),
("hCursor", HCURSOR),
("hbrBackground", wt.HBRUSH),
("lpszMenuName", wt.LPCWSTR),
("lpszClassName", wt.LPCWSTR),
("hIconSm", wt.HICON),
)
WNDCLASSEX = WNDCLASSEXW
class RawInputDevice(Struct):
_fields_ = (
("usUsagePage", wt.USHORT),
("usUsage", wt.USHORT),
("dwFlags", wt.DWORD),
("hwndTarget", wt.HWND),
)
PRawInputDevice = ct.POINTER(RawInputDevice)
class RAWINPUTHEADER(Struct):
_fields_ = (
("dwType", wt.DWORD),
("dwSize", wt.DWORD),
("hDevice", wt.HANDLE),
("wParam", wt.WPARAM),
)
class RAWMOUSE(Struct):
_fields_ = (
("usFlags", wt.USHORT),
("ulButtons", wt.ULONG), # unnamed union: 2 USHORTS: flags, data
("ulRawButtons", wt.ULONG),
("lLastX", wt.LONG),
("lLastY", wt.LONG),
("ulExtraInformation", wt.ULONG),
)
class RAWKEYBOARD(Struct):
_fields_ = (
("MakeCode", wt.USHORT),
("Flags", wt.USHORT),
("Reserved", wt.USHORT),
("VKey", wt.USHORT),
("Message", wt.UINT),
("ExtraInformation", wt.ULONG),
)
class RAWHID(Struct):
_fields_ = (
("dwSizeHid", wt.DWORD),
("dwCount", wt.DWORD),
("bRawData", wt.BYTE * 1), # @TODO - cfati: https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-rawhid, but not very usable via CTypes
)
class RAWINPUT_U0(Uni):
_fields_ = (
("mouse", RAWMOUSE),
("keyboard", RAWKEYBOARD),
("hid", RAWHID),
)
class RAWINPUT(Struct):
_fields_ = (
("header", RAWINPUTHEADER),
("data", RAWINPUT_U0),
)
PRAWINPUT = ct.POINTER(RAWINPUT)
GetLastError = kernel32.GetLastError
GetLastError.argtypes = ()
GetLastError.restype = wt.DWORD
GetModuleHandle = kernel32.GetModuleHandleW
GetModuleHandle.argtypes = (wt.LPWSTR,)
GetModuleHandle.restype = wt.HMODULE
DefWindowProc = user32.DefWindowProcW
DefWindowProc.argtypes = wndproc_args
DefWindowProc.restype = LRESULT
RegisterClassEx = user32.RegisterClassExW
RegisterClassEx.argtypes = (ct.POINTER(WNDCLASSEX),)
RegisterClassEx.restype = wt.ATOM
CreateWindowEx = user32.CreateWindowExW
CreateWindowEx.argtypes = (wt.DWORD, wt.LPCWSTR, wt.LPCWSTR, wt.DWORD, ct.c_int, ct.c_int, ct.c_int, ct.c_int, wt.HWND, wt.HMENU, wt.HINSTANCE, wt.LPVOID)
CreateWindowEx.restype = wt.HWND
RegisterRawInputDevices = user32.RegisterRawInputDevices
RegisterRawInputDevices.argtypes = (PRawInputDevice, wt.UINT, wt.UINT)
RegisterRawInputDevices.restype = wt.BOOL
GetRawInputData = user32.GetRawInputData
GetRawInputData.argtypes = (PRAWINPUT, wt.UINT, wt.LPVOID, wt.PUINT, wt.UINT)
GetRawInputData.restype = wt.UINT
GetMessage = user32.GetMessageW
GetMessage.argtypes = (wt.LPMSG, wt.HWND, wt.UINT, wt.UINT)
GetMessage.restype = wt.BOOL
PeekMessage = user32.PeekMessageW
PeekMessage.argtypes = (wt.LPMSG, wt.HWND, wt.UINT, wt.UINT, wt.UINT)
PeekMessage.restype = wt.BOOL
TranslateMessage = user32.TranslateMessage
TranslateMessage.argtypes = (wt.LPMSG,)
TranslateMessage.restype = wt.BOOL
DispatchMessage = user32.DispatchMessageW
DispatchMessage.argtypes = (wt.LPMSG,)
DispatchMessage.restype = LRESULT
PostQuitMessage = user32.PostQuitMessage
PostQuitMessage.argtypes = (ct.c_int,)
PostQuitMessage.restype = None
代碼00.py:
#!/usr/bin/env python
import ctypes as ct
import ctypes.wintypes as wt
import sys
import time
import ctypes_wrappers as cw
HWND_MESSAGE = -3
WM_QUIT = 0x0012
WM_INPUT = 0x00FF
WM_KEYUP = 0x0101
WM_CHAR = 0x0102
HID_USAGE_PAGE_GENERIC = 0x01
RIDEV_NOLEGACY = 0x00000030
RIDEV_INPUTSINK = 0x00000100
RIDEV_CAPTUREMOUSE = 0x00000200
RID_HEADER = 0x10000005
RID_INPUT = 0x10000003
RIM_TYPEMOUSE = 0
RIM_TYPEKEYBOARD = 1
RIM_TYPEHID = 2
PM_NOREMOVE = 0x0000
def wnd_proc(hwnd, msg, wparam, lparam):
print(f"Handle message - hwnd: 0x{hwnd:016X} msg: 0x{msg:08X} wp: 0x{wparam:016X} lp: 0x{lparam:016X}")
if msg == WM_INPUT:
size = wt.UINT(0)
res = cw.GetRawInputData(ct.cast(lparam, cw.PRAWINPUT), RID_INPUT, None, ct.byref(size), ct.sizeof(cw.RAWINPUTHEADER))
if res == wt.UINT(-1) or size == 0:
print_error(text="GetRawInputData 0")
return 0
buf = ct.create_string_buffer(size.value)
res = cw.GetRawInputData(ct.cast(lparam, cw.PRAWINPUT), RID_INPUT, buf, ct.byref(size), ct.sizeof(cw.RAWINPUTHEADER))
if res != size.value:
print_error(text="GetRawInputData 1")
return 0
#print("kkt: ", ct.cast(lparam, cw.PRAWINPUT).contents.to_string())
ri = ct.cast(buf, cw.PRAWINPUT).contents
#print(ri.to_string())
head = ri.header
print(head.to_string())
#print(ri.data.mouse.to_string())
#print(ri.data.keyboard.to_string())
#print(ri.data.hid.to_string())
if head.dwType == RIM_TYPEMOUSE:
data = ri.data.mouse
elif head.dwType == RIM_TYPEKEYBOARD:
data = ri.data.keyboard
if data.VKey == 0x1B:
cw.PostQuitMessage(0)
elif head.dwType == RIM_TYPEHID:
data = ri.data.hid
else:
print("Wrong raw input type!!!")
return 0
print(data.to_string())
return cw.DefWindowProc(hwnd, msg, wparam, lparam)
def print_error(code=None, text=None):
text = text " - e" if text else "E"
code = cw.GetLastError() if code is None else code
print(f"{text}rror code: {code}")
def register_devices(hwnd=None):
flags = RIDEV_INPUTSINK # @TODO - cfati: If setting to 0, GetMessage hangs
generic_usage_ids = (0x01, 0x02, 0x04, 0x05, 0x06, 0x07, 0x08)
devices = (cw.RawInputDevice * len(generic_usage_ids))(
*(cw.RawInputDevice(HID_USAGE_PAGE_GENERIC, uid, flags, hwnd) for uid in generic_usage_ids)
)
#for d in devices: print(d.usUsagePage, d.usUsage, d.dwFlags, d.hwndTarget)
if cw.RegisterRawInputDevices(devices, len(generic_usage_ids), ct.sizeof(cw.RawInputDevice)):
print("Successfully registered input device(s)!")
return True
else:
print_error(text="RegisterRawInputDevices")
return False
def main(*argv):
wnd_cls = "SO049572093_RawInputWndClass"
wcx = cw.WNDCLASSEX()
wcx.cbSize = ct.sizeof(cw.WNDCLASSEX)
#wcx.lpfnWndProc = ct.cast(cw.DefWindowProc, ct.c_void_p)
wcx.lpfnWndProc = cw.WNDPROC(wnd_proc)
wcx.hInstance = cw.GetModuleHandle(None)
wcx.lpszClassName = wnd_cls
#print(dir(wcx))
res = cw.RegisterClassEx(ct.byref(wcx))
if not res:
print_error(text="RegisterClass")
return 0
hwnd = cw.CreateWindowEx(0, wnd_cls, None, 0, 0, 0, 0, 0, 0, None, wcx.hInstance, None)
if not hwnd:
print_error(text="CreateWindowEx")
return 0
#print("hwnd:", hwnd)
if not register_devices(hwnd):
return 0
msg = wt.MSG()
pmsg = ct.byref(msg)
print("Start loop (press <ESC> to exit)...")
while res := cw.GetMessage(pmsg, None, 0, 0):
if res < 0:
print_error(text="GetMessage")
break
cw.TranslateMessage(pmsg)
cw.DispatchMessage(pmsg)
if __name__ == "__main__":
print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
64 if sys.maxsize > 0x100000000 else 32, sys.platform))
rc = main(*sys.argv[1:])
print("\nDone.")
sys.exit(rc)
輸出:
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q071994439]> "e:\Work\Dev\VEnvs\py_pc064_03.09_test0\Scripts\python.exe" code00.py Python 3.9.9 (tags/v3.9.9:ccb0e6a, Nov 15 2021, 18:08:50) [MSC v.1929 64 bit (AMD64)] 064bit on win32 Handle message - hwnd: 0x00000000002F0606 msg: 0x00000024 wp: 0x0000000000000000 lp: 0x000000F5E0BEDDE0 Handle message - hwnd: 0x00000000002F0606 msg: 0x00000081 wp: 0x0000000000000000 lp: 0x000000F5E0BEDD70 Handle message - hwnd: 0x00000000002F0606 msg: 0x00000083 wp: 0x0000000000000000 lp: 0x000000F5E0BEDE00 Handle message - hwnd: 0x00000000002F0606 msg: 0x00000001 wp: 0x0000000000000000 lp: 0x000000F5E0BEDD70 Successfully registered input device(s)! Start loop (press <ESC> to exit)... Handle message - hwnd: 0x00000000002F0606 msg: 0x0000031F wp: 0x0000000000000001 lp: 0x0000000000000000 Handle message - hwnd: 0x00000000002F0606 msg: 0x000000FF wp: 0x0000000000000001 lp: 0x-00000003849FCDF RAWINPUTHEADER (size: 24) instance at 0x00000296313BBBC0: dwType: 1 dwSize: 40 hDevice: 843780541 wParam: 1 RAWKEYBOARD (size: 16) instance at 0x00000296313BBCC0: MakeCode: 30 Flags: 0 Reserved: 0 VKey: 65 Message: 256 ExtraInformation: 0 Handle message - hwnd: 0x00000000002F0606 msg: 0x000000FF wp: 0x0000000000000001 lp: 0x0000000031AE1619 RAWINPUTHEADER (size: 24) instance at 0x00000296313BBBC0: dwType: 1 dwSize: 40 hDevice: 843780541 wParam: 1 RAWKEYBOARD (size: 16) instance at 0x00000296313BBD40: MakeCode: 30 Flags: 1 Reserved: 0 VKey: 65 Message: 257 ExtraInformation: 0 Handle message - hwnd: 0x00000000002F0606 msg: 0x000000FF wp: 0x0000000000000001 lp: 0x000000007C851501 RAWINPUTHEADER (size: 24) instance at 0x00000296313BBBC0: dwType: 0 dwSize: 48 hDevice: 4461491 wParam: 1 RAWMOUSE (size: 24) instance at 0x00000296313BBDC0: usFlags: 0 ulButtons: 1 ulRawButtons: 0 lLastX: 0 lLastY: 0 ulExtraInformation: 0 Handle message - hwnd: 0x00000000002F0606 msg: 0x000000FF wp: 0x0000000000000001 lp: 0x0000000031B41619 RAWINPUTHEADER (size: 24) instance at 0x00000296313BBBC0: dwType: 0 dwSize: 48 hDevice: 4461491 wParam: 1 RAWMOUSE (size: 24) instance at 0x00000296313BBE40: usFlags: 0 ulButtons: 2 ulRawButtons: 0 lLastX: 0 lLastY: 0 ulExtraInformation: 0 Handle message - hwnd: 0x00000000002F0606 msg: 0x000000FF wp: 0x0000000000000001 lp: 0x0000000052D10665 RAWINPUTHEADER (size: 24) instance at 0x00000296313BBBC0: dwType: 1 dwSize: 40 hDevice: 843780541 wParam: 1 RAWKEYBOARD (size: 16) instance at 0x00000296313BBEC0: MakeCode: 1 Flags: 0 Reserved: 0 VKey: 27 Message: 256 ExtraInformation: 0 Done.
備注:
(以上)輸出由以下操作生成:a、LClick、ESC
我放棄了PyWin32,因為它不包含我們需要的函式(不屬于Raw Input系列),但它可能(至少)用于win32con中的常量(以避免定義它們)
我認為可以通過將功能從wnd_proc移動到main的while回圈來簡化事情(因此可以洗掉所有視窗類的東西(常量、結構、函式)),但我是這樣開始的,我不喜歡改變它
一個重大突破是RIDEV_INPUTSINK(從那時起,GetMessage停止掛起)
RAWHID結構(@TODO)被“按書”包裝,但它不會在OOTB中作業(你提到過使用其他型別的設備)。最后的(1 大小)陣列只是說明將跟隨一些附加資料(dwSizeHid大小)的一種方式,這些資料顯然不適合一個位元組。那里需要一個“技巧”:必須根據資料的大小動態定義結構(例如:[SO]:在 ctypes.Structure 中動態設定_fields_(@CristiFati 的答案) -我記得我寫了一個更新的答案關于那個主題,但我找不到或不記得它),以及所有這些行為(遞回)在封裝它的所有結構(聯合)中傳播
uj5u.com熱心網友回復:
GetMessage 用于訊息泵,請參閱https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessage。我不知道你應該使用什么,但 GetMessage 絕對不適合這種用途。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/470862.html
上一篇:如何監控內核回呼
下一篇:如何更改視窗滾動條的位置?