python - 在 Windows 上使用 python 进行 USB 热插拔回调
问题描述
是否可以编写一个 python 脚本,以便在 Windows 上添加或删除 USB 设备时调用一个函数?
libusb
(以及相应的 python 模块,例如libusb1
)似乎是最流行的解决方案,但它缺乏 Windows 中的热插拔回调注册支持。自 2015 年以来,已为此提出功能请求,但仍未实施。
我已经看到一些黑客每隔一段时间查询 Windows 的 USB 设备,将当前设备列表与之前的设备列表进行比较,并将其用作替代方案。由于我的应用程序的性质,这种 hack 将是一个巨大的安全风险,并且不是一个有效的解决方案。我需要对 USB 热插拔事件进行实际回调注册。
虽然不太理想,但我愿意用 C 或 C++ 编写一些东西,然后在必要时编写一个与该代码的 python 绑定。
在 Windows 上连接或断开 USB 设备时是否可以调用 python 函数?
解决方案
是的,您可以在 python 中使用 ctypes 来注册WM_DEVICECHANGE
消息的回调。
我成功地在linux(使用libusb1
python模块)和windows(使用ctypes
python模块)中向usb hotplug事件添加了注册回调函数。大部分相关的 windows 代码可以在这里找到:
这主要基于在这些链接中找到的代码:
- http://timgolden.me.uk/python/win32_how_do_i/detect-device-insertion.html
- 在 Python 中检测 Windows 上的媒体插入
import win32api, win32con, win32gui
from ctypes import *
#
# Device change events (WM_DEVICECHANGE wParam)
#
DBT_DEVICEARRIVAL = 0x8000
DBT_DEVICEQUERYREMOVE = 0x8001
DBT_DEVICEQUERYREMOVEFAILED = 0x8002
DBT_DEVICEMOVEPENDING = 0x8003
DBT_DEVICEREMOVECOMPLETE = 0x8004
DBT_DEVICETYPESSPECIFIC = 0x8005
DBT_CONFIGCHANGED = 0x0018
#
# type of device in DEV_BROADCAST_HDR
#
DBT_DEVTYP_OEM = 0x00000000
DBT_DEVTYP_DEVNODE = 0x00000001
DBT_DEVTYP_VOLUME = 0x00000002
DBT_DEVTYPE_PORT = 0x00000003
DBT_DEVTYPE_NET = 0x00000004
#
# media types in DBT_DEVTYP_VOLUME
#
DBTF_MEDIA = 0x0001
DBTF_NET = 0x0002
WORD = c_ushort
DWORD = c_ulong
class DEV_BROADCAST_HDR(Structure):
_fields_ = [
("dbch_size", DWORD),
("dbch_devicetype", DWORD),
("dbch_reserved", DWORD)
]
class DEV_BROADCAST_VOLUME(Structure):
_fields_ = [
("dbcv_size", DWORD),
("dbcv_devicetype", DWORD),
("dbcv_reserved", DWORD),
("dbcv_unitmask", DWORD),
("dbcv_flags", WORD)
]
def drive_from_mask(mask):
n_drive = 0
while 1:
if (mask & (2 ** n_drive)):
return n_drive
else:
n_drive += 1
class Notification:
def __init__(self):
message_map = {
win32con.WM_DEVICECHANGE: self.onDeviceChange
}
wc = win32gui.WNDCLASS()
hinst = wc.hInstance = win32api.GetModuleHandle(None)
wc.lpszClassName = "DeviceChangeDemo"
wc.style = win32con.CS_VREDRAW | win32con.CS_HREDRAW
wc.hCursor = win32gui.LoadCursor(0, win32con.IDC_ARROW)
wc.hbrBackground = win32con.COLOR_WINDOW
wc.lpfnWndProc = message_map
classAtom = win32gui.RegisterClass(wc)
style = win32con.WS_OVERLAPPED | win32con.WS_SYSMENU
self.hwnd = win32gui.CreateWindow(
classAtom,
"Device Change Demo",
style,
0, 0,
win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT,
0, 0,
hinst, None
)
def onDeviceChange(self, hwnd, msg, wparam, lparam):
#
# WM_DEVICECHANGE:
# wParam - type of change: arrival, removal etc.
# lParam - what's changed?
# if it's a volume then...
# lParam - what's changed more exactly
#
dev_broadcast_hdr = DEV_BROADCAST_HDR.from_address(lparam)
if wparam == DBT_DEVICEARRIVAL:
print("Something's arrived")
if dev_broadcast_hdr.dbch_devicetype == DBT_DEVTYP_VOLUME:
print("It's a volume!")
dev_broadcast_volume = DEV_BROADCAST_VOLUME.from_address(lparam)
if dev_broadcast_volume.dbcv_flags & DBTF_MEDIA:
print("with some media")
drive_letter = drive_from_mask(dev_broadcast_volume.dbcv_unitmask)
print("in drive", chr(ord("A") + drive_letter))
return 1
if __name__ == '__main__':
w = Notification()
win32gui.PumpMessages()
推荐阅读
- c - 如何引用在 .h 文件中初始化的二维数组并在 .c 函数中使用它?
- java - 在 Ubuntu 18.04 上安装 Tomcat 9.0.16
- reactjs - A-frame-react:.map() 时如何传递值?
- arrays - Bash 脚本“分配数组变量的另一种方式”
- delphi - 为什么这两个 Delphi 代码块的行为不同?
- javascript - 如何在一天中的特定时间自动刷新 wordpress 页面
- highcharts - 在最后的 Highcharts 行中隐藏或禁用图例或标签
- r - R:如何“合并()”整个数据表
- c++ - 架构 x86_64 的未定义符号:“operator<<”
- javascript - 如何在 Javascript 中进行 2 个连续的 API 调用?