Created
September 23, 2025 08:29
-
-
Save BHznJNs/7f8004de37a4a561b548d827e669f7d9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import ctypes | |
| import win32api | |
| import win32con | |
| import win32gui | |
| import pywintypes | |
| from ctypes import wintypes | |
| # 1. 使用 ctypes 定义 pywin32 未封装的 API 和结构体 (已完全修正) | |
| # ================================================================= | |
| # --- 常量定义 --- | |
| WM_INPUT = 0x00FF | |
| RIDEV_INPUTSINK = 0x00000100 | |
| RID_INPUT = 0x10000003 | |
| RIM_TYPEMOUSE = 0 | |
| # --- 结构体定义 (精确匹配 C 语言内存布局) --- | |
| class RAWINPUTDEVICE(ctypes.Structure): | |
| _fields_ = [ | |
| ("usUsagePage", wintypes.USHORT), | |
| ("usUsage", wintypes.USHORT), | |
| ("dwFlags", wintypes.DWORD), | |
| ("hwndTarget", wintypes.HWND), | |
| ] | |
| # RAWMOUSE 内部的 button data 是一个联合体 | |
| class _RAWMOUSE_STRUCT(ctypes.Structure): | |
| _fields_ = [ | |
| ("usButtonFlags", wintypes.USHORT), | |
| ("usButtonData", wintypes.USHORT), | |
| ] | |
| class _RAWMOUSE_UNION(ctypes.Union): | |
| _fields_ = [ | |
| ("ulButtons", wintypes.ULONG), | |
| ("struct", _RAWMOUSE_STRUCT), | |
| ] | |
| class RAWMOUSE(ctypes.Structure): | |
| _fields_ = [ | |
| ("usFlags", wintypes.USHORT), | |
| ("_buttons", _RAWMOUSE_UNION), # 匿名字段 | |
| ("ulRawButtons", wintypes.ULONG), | |
| ("lLastX", wintypes.LONG), | |
| ("lLastY", wintypes.LONG), | |
| ("ulExtraInformation", wintypes.ULONG), | |
| ] | |
| _anonymous_ = ("_buttons",) # 允许直接访问 union 内部成员 | |
| # RAWINPUT 内部的 data 也是一个联合体 | |
| class RAWKEYBOARD(ctypes.Structure): # 占位符 | |
| _fields_ = [("data", wintypes.BYTE * 20)] | |
| class RAWHID(ctypes.Structure): # 占位符 | |
| _fields_ = [("data", wintypes.BYTE * 8)] | |
| class RAWINPUT_DATA(ctypes.Union): | |
| _fields_ = [ | |
| ("mouse", RAWMOUSE), | |
| ("keyboard", RAWKEYBOARD), | |
| ("hid", RAWHID), | |
| ] | |
| class RAWINPUTHEADER(ctypes.Structure): | |
| _fields_ = [ | |
| ("dwType", wintypes.DWORD), | |
| ("dwSize", wintypes.DWORD), | |
| ("hDevice", wintypes.HANDLE), | |
| ("wParam", wintypes.WPARAM), | |
| ] | |
| class RAWINPUT(ctypes.Structure): | |
| _fields_ = [ | |
| ("header", RAWINPUTHEADER), | |
| ("data", RAWINPUT_DATA), | |
| ] | |
| # --- 从 user32.dll 加载函数 --- | |
| user32 = ctypes.windll.user32 | |
| RegisterRawInputDevices = user32.RegisterRawInputDevices | |
| RegisterRawInputDevices.argtypes = [ctypes.POINTER(RAWINPUTDEVICE), wintypes.UINT, wintypes.UINT] | |
| RegisterRawInputDevices.restype = wintypes.BOOL | |
| GetRawInputData = user32.GetRawInputData | |
| # 2. 使用 pywin32 编写窗口和消息循环的主体逻辑 | |
| # ================================================================= | |
| def wnd_proc(hwnd, msg, wparam, lparam): | |
| if msg == WM_INPUT: | |
| size = wintypes.UINT(0) | |
| GetRawInputData(lparam, RID_INPUT, None, ctypes.byref(size), ctypes.sizeof(RAWINPUTHEADER)) | |
| buf = ctypes.create_string_buffer(size.value) | |
| if GetRawInputData(lparam, RID_INPUT, buf, ctypes.byref(size), ctypes.sizeof(RAWINPUTHEADER)) == size.value: | |
| raw_input = ctypes.cast(buf, ctypes.POINTER(RAWINPUT)).contents | |
| if raw_input.header.dwType == RIM_TYPEMOUSE: | |
| # --- 修正访问方式 --- | |
| # 因为 data 是一个 union,需要先指定要访问的成员 (mouse) | |
| mouse_data = raw_input.data.mouse | |
| print(f"Relative Mouse Movement: dX={mouse_data.lLastX}, dY={mouse_data.lLastY}") | |
| return win32gui.DefWindowProc(hwnd, msg, wparam, lparam) | |
| def main(): | |
| wnd_class = win32gui.WNDCLASS() | |
| wnd_class.lpszClassName = "RawInputWindowClassFinal" | |
| wnd_class.lpfnWndProc = wnd_proc | |
| try: | |
| win32gui.RegisterClass(wnd_class) | |
| except win32gui.error as e: | |
| if e.winerror != 1410: # ERROR_CLASS_ALREADY_EXISTS | |
| raise e | |
| hwnd = win32gui.CreateWindow( | |
| wnd_class.lpszClassName, "Raw Input Test Window", | |
| 0, 0, 0, 0, 0, None, None, | |
| win32api.GetModuleHandle(None), None | |
| ) | |
| if not hwnd: | |
| raise RuntimeError("Failed to create window.") | |
| device = RAWINPUTDEVICE(1, 2, RIDEV_INPUTSINK, hwnd) | |
| if not RegisterRawInputDevices(ctypes.byref(device), 1, ctypes.sizeof(RAWINPUTDEVICE)): | |
| raise RuntimeError(f"Failed to register raw input devices. Error code: {ctypes.get_last_error()}") | |
| print("正在捕获原始鼠标移动数据... 按 Ctrl+C 停止。") | |
| win32gui.PumpMessages() | |
| if __name__ == "__main__": | |
| try: | |
| main() | |
| except KeyboardInterrupt: | |
| print("\n程序已停止。") | |
| except (RuntimeError, pywintypes.error) as e: | |
| print(f"发生错误:{e}") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment