pywin32是一个广泛使用的 Python 扩展库用于在 Windows 平台上调用 Win32 API、操作 COM 对象、访问 Windows 服务、注册表、事件日志、GUI 控件如窗口、消息等。它并非直接封装所有 Windows API而是通过 Pythonic 的方式如类、方法、属性对底层 Windows SDK 功能进行高层抽象和封装极大简化了 Windows 系统级编程。核心功能包括Windows API 调用通过win32api、win32gui、win32con、win32event等模块提供对常用 API 的封装如FindWindow,SendMessage,GetLastError,RegOpenKeyEx等COM 自动化支持通过win32com.client可轻松调用 Excel、Word、Outlook 等 Office 应用或自定义 COM 组件服务管理win32service和win32serviceutil支持创建、启动、控制 Windows 服务事件日志与安全win32evtlog、win32security提供日志读写、ACL、令牌操作等进程/线程/同步对象win32process、win32event支持进程创建、优先级设置、互斥体、信号量等注册表操作win32con定义常量win32api.Reg*系列函数提供安全便捷的注册表读写。安装方式pipinstallpywin32注意安装后需运行python Scripts/pywin32_postinstall.py -install路径可能因 Python 环境而异以注册 COM 类型库和系统服务支持。示例获取当前前台窗口标题importwin32gui hwndwin32gui.GetForegroundWindow()titlewin32gui.GetWindowText(hwnd)print(f当前窗口标题{title})⚠️ 注意事项pywin32 不是官方 Python 标准库需单独安装部分功能需管理员权限如服务操作、全局钩子、注册表 HKEY_LOCAL_MACHINE 写入64 位 Python 需匹配 64 位 pywin32自动适配但 COM 交互时需注意位数一致性如调用 32 位 Excel 进程需 32 位 Python文档较分散推荐查阅 pywin32 GitHub 及内置帮助help(win32api)或PyWin32.chm帮助文件。在 Windows 上使用pywin32实现全局键盘/鼠标钩子Global Hook是可行的但需特别注意pywin32本身不直接提供全局钩子的高层封装如SetWindowsHookEx的完整 Python 接口其win32gui和win32api模块虽可调用底层 API但缺少对钩子回调函数HookProc跨线程/跨进程正确注册与消息循环的支持——尤其是全局钩子WH_KEYBOARD_LL/WH_MOUSE_LL要求回调必须位于有消息循环的线程中且低级钩子LL虽不要求 DLL 注入但仍需正确处理CallNextHookEx和避免阻塞。✅ 正确、稳定、推荐的方式是结合pywin32用于窗口/消息基础操作 ctypes直接调用user32.dll的SetWindowsHookExW/UnhookWindowsHookEx/CallNextHookEx 主线程运行GetMessage消息循环或使用win32event同步。⚠️pywin32单独无法安全实现全局钩子常见误区是误以为win32gui.SetWindowsHookEx存在它不存在—— pywin32 并未封装该 API。✅ 正确示例使用ctypespywin32辅助实现全局低级键盘钩子监听所有按键importctypesfromctypesimportwintypesimportwin32event# 用于同步/退出控制importpythoncom# 若需 COM 支持本例非必需但常共用importsys# 定义 Windows 常量来自 WinUser.hWH_KEYBOARD_LL13HC_ACTION0VK_ESCAPE0x1B# 钩子回调函数类型stdcall64位需注意调用约定HHOOKwintypes.HHOOK LRESULTwintypes.LRESULT WPARAMwintypes.WPARAM LPARAMwintypes.LPARAM# 键盘结构体LL 钩子 LPARAM 指向 KBDLLHOOKSTRUCTclassKBDLLHOOKSTRUCT(ctypes.Structure):_fields_[(vkCode,wintypes.DWORD),(scanCode,wintypes.DWORD),(flags,wintypes.DWORD),(time,wintypes.DWORD),(dwExtraInfo,ctypes.POINTER(wintypes.ULONG)),]# 全局变量存储钩子句柄避免被 GC 回收hook_handleNoneexit_eventwin32event.CreateEvent(None,0,0,None)# 钩子回调函数必须为 stdcall且不能是 lambda 或内联函数defkeyboard_proc(nCode,wParam,lParam):ifnCodeHC_ACTION:ifwParam0x100:# WM_KEYDOWNkb_structKBDLLHOOKSTRUCT.from_address(lParam)vkkb_struct.vkCodeprint(fKey down: VK{vk}(0x{vk:X}))ifvkVK_ESCAPE:print(ESC pressed → exiting...)win32event.SetEvent(exit_event)# 必须调用 CallNextHookEx否则其他程序收不到按键returnctypes.windll.user32.CallNextHookEx(hook_handle,nCode,wParam,lParam)# 设置钩子需在有消息循环的线程中调用definstall_hook():globalhook_handle# 获取当前线程 ID全局钩子需为 0表示系统级hmodctypes.windll.kernel32.GetModuleHandleW(None)# 注意keyboard_proc 必须是 ctypes.CFUNCTYPE 绑定的可调用对象CMPFUNCctypes.CFUNCTYPE(LRESULT,WPARAM,WPARAM,LPARAM)procCMPFUNC(keyboard_proc)hook_handlectypes.windll.user32.SetWindowsHookExW(WH_KEYBOARD_LL,proc,hmod,0# dwThreadId 0 → 全局钩子)ifnothook_handle:raiseRuntimeError(fFailed to set keyboard hook, error{ctypes.windll.kernel32.GetLastError()})returnproc# 保持引用防止被 GC 回收# 主消息循环关键必须运行以接收系统钩子消息defrun_message_loop():msgwintypes.MSG()whileTrue:# 等待消息或退出事件超时 100ms 避免忙等retwin32event.WaitForSingleObject(exit_event,100)ifretwin32event.WAIT_OBJECT_0:break# PeekMessage 非阻塞检查也可用 GetMessage 阻塞式但需配合退出逻辑ifctypes.windll.user32.PeekMessageW(ctypes.byref(msg),None,0,0,1):ctypes.windll.user32.TranslateMessage(ctypes.byref(msg))ctypes.windll.user32.DispatchMessageW(ctypes.byref(msg))if__name____main__:try:cb_funcinstall_hook()print(Global keyboard hook installed. Press ESC to exit.)run_message_loop()finally:ifhook_handle:ctypes.windll.user32.UnhookWindowsHookEx(hook_handle)print(Hook uninstalled.)关键要点说明✅ 使用ctypes直接调用user32.dll是唯一可靠方式✅keyboard_proc必须是CFUNCTYPE绑定的持久函数对象否则被 GC 后崩溃✅ 必须在主线程运行消息循环PeekMessage/GetMessage否则钩子不触发✅ 全局低级钩子WH_KEYBOARD_LL无需 DLL 注入但需win32gui/win32api之外的ctypes支持❌pywin32不提供SetWindowsHookEx封装不可依赖win32api或win32gui实现钩子⚠️ 全局钩子可能被 UAC、杀毒软件拦截建议以管理员权限运行 高频钩子如鼠标移动需避免耗时操作否则拖慢系统输入响应。 替代方案更简单 更安全若仅需监听当前进程内按键非全局可用win32guiSetWindowsHook局部钩子或pynput跨平台、纯 Python、自动处理钩子生命周期pipinstallpynputfrompynputimportkeyboarddefon_press(key):try:print(fKey{key.char}pressed)exceptAttributeError:print(fSpecial key{key}pressed)listenerkeyboard.Listener(on_presson_press)listener.start()listener.join()# 或 listener.stop() 控制✅pynput内部已用ctypes正确封装钩子推荐日常使用。