• Pythen 监听 USB 存储器插入、拔出操作


    Windows 设备管理事件

    WM_DEVICECHANGE 标识符

    事件 说明
    DBT_DEVNODES_CHANGED 0x0007 已向系统添加或删除设备。
    DBT_DEVICEARRIVAL 0x8000 当插入设备或媒体块并变为可用时,系统会广播DBT_DEVICEARRIVAL设备事件。
    DBT_DEVICEQUERYREMOVE 0x8001 系统广播DBT_DEVICEQUERYREMOVE设备事件以请求删除设备或媒体块的权限。 此消息是应用程序和驱动程序准备进行此删除的最后一次机会。 但是,任何应用程序都可以拒绝此请求并取消操作。
    DBT_DEVICEQUERYREMOVEFAILED 0x8002 系统在取消删除设备或媒体块的请求时广播DBT_DEVICEQUERYREMOVEFAILED设备事件。
    DBT_DEVICEMOVEPENDING 0x8003 系统在删除设备或媒体块且不再可供使用时广播DBT_DEVICEREMOVEPENDING设备事件。
    DBT_DEVICEREMOVECOMPLETE 0x8004 当设备或媒体被物理删除时,系统会广播DBT_DEVICEREMOVECOMPLETE设备事件。
    DBT_DEVICETYPESSPECIFIC 0x8005 系统在发生特定于设备的事件时,广播DBT_DEVICETYPESPECIFIC设备事件。
    DBT_CONFIGCHANGED 0x0018 系统广播DBT_CONFIGCHANGED设备事件,以指示由于停靠或取消停靠而更改了当前配置。 在HKEY_CURRENT_CONFIG键下的注册表中存储数据的应用程序或驱动程序应更新数据。

    实例

    USB 非存储设备

    设备类型 是否首次插入 插入注册触发信号次数 拔出销毁触发信号次数
    USB 有线键盘 4 2
    USB 有线键盘 3 2
    USB 2.4G 无线接收器(键盘、鼠标) 5 2
    USB 2.4G 无线接收器(键盘、鼠标) 3 2
    USB 有线键盘

    首次插入流程:

    # 第一个或为电平变化。USB 设备拔出,线路短路引发电平变化,有主控器识别,判断设备变化,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    # 第二个或为控制传输。由主控器发送给目标设备,目标设备返回地址、端口号、设备描述符、设备类型等,目标设备注册,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    # 第三个或为设备启动。由主控器识别为 USB 输入设备,触发 DBT_DEVICEARRIVAL 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    # 第四个或为更新索引。目标设备索引读取完成,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    

    非首次插入流程:

    # 第一个或为电平变化。USB 设备拔出,线路短路引发电平变化,有主控器识别,判断设备变化,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    # 第二个或为设备启动。无需注册,由主控器识别为 USB 输入设备,触发 DBT_DEVICEARRIVAL 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    # 第三个个或为更新索引。目标设备索引读取完成,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    

    拔出流程:

    # 第一个或为电平变化。USB 设备拔出,线路短路引发电平变化,有主控器识别,判断设备变化,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    # 第二个或为设备列表更新。触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    USB 2.4G 无线接收器(键盘、鼠标)

    首次插入流程:

    # 第一个或为电平变化。USB 设备拔出,线路短路引发电平变化,有主控器识别,判断设备变化,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    # 第二个或为控制传输。由主控器发送给目标设备,目标设备返回地址、端口号、设备描述符、设备类型等,目标设备注册,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    # 第三个或为驱动安装。由主控器发送给目标设备信息,目标设备安装驱动,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    # 第四个或为设备启动。由主控器识别为 USB 输入设备,触发 DBT_DEVICEARRIVAL 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    # 第五个或为更新索引。目标设备索引读取完成,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    

    非首次插入流程:

    # 第一个或为电平变化。USB 设备拔出,线路短路引发电平变化,有主控器识别,判断设备变化,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    # 第二个或为设备可用。无需注册,鼠标驱动安装在主机中,主机与目标设备通信通过厂商的驱动进行,由驱动识别设备可用性,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    # 第三个个或为更新索引。目标设备索引读取完成,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    

    拔出流程:

    # 第一个或为电平变化。USB 设备拔出,线路短路引发电平变化,有主控器识别,判断设备变化,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    # 第二个或为设备列表更新。触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    

    USB 存储设备

    设备类型 是否首次插入 插入注册触发信号次数 拔出销毁触发信号次数
    USB 2.0 U 盘 是/否 5 3
    USB 3.0 U 盘 是/否 4 3

    下述流程不一定对,有错误请指出或辩证看待。

    USB 2.0 U 盘

    插入流程:

    # 第一个或为控制传输。USB 设备插入,线路短接引发电平变化,有主控器识别,判断设备变化,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    # 第二个或为控制传输。由主控器发送给目标设备,目标设备返回地址、端口号、设备描述符、设备类型等,目标设备注册,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    # 第三个或为磁盘可用。由主控器划分盘符,主机识别为 USB 存储设备,触发 DBT_DEVICEARRIVAL 广播。
    wparam:|DBT_DEVICEARRIVAL:0x8000|
    lparam:|0xa27b5ef200|
    drive_letter:|5|
    ch( ord('A') + drive_letter):|F|
    
    # 第四个或为设备列表更新。触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    # 第五个或为更新磁盘索引。受限于协议或速度,目标设备索引读取完成,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    

    拔出流程:

    # 第一个或为电平变化。USB 设备拔出,线路短路引发电平变化,有主控器识别,判断设备变化,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    # 第二个或为设备销毁。由主控器发送给目标设备,目标设备未响应,由主控器移除盘符,确认已删除设备或媒体片段,触发 DBT_DEVICEREMOVECOMPLETE 广播。
    wparam:|DBT_DEVICEREMOVECOMPLETE:0x8004|
    lparam:|0xa27b5ef200|
    drive_letter:|5|
    ch( ord('A') + drive_letter):|F|
    
    # 第三个或为设备列表更新。触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    USB 3.0 U 盘

    插入流程:

    # 第一个或为控制传输。USB 设备插入,线路短接引发电平变化,有主控器识别,判断设备变化,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    # 第二个或为控制传输。由主控器发送给目标设备,目标设备返回地址、端口号、设备描述符、设备类型等,目标设备注册,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    # 第三个或为磁盘可用。由主控器划分盘符,主机识别为 USB 存储设备,触发 DBT_DEVICEARRIVAL 广播。
    wparam:|DBT_DEVICEARRIVAL:0x8000|
    lparam:|0xa27b5ef200|
    drive_letter:|5|
    ch( ord('A') + drive_letter):|F|
    
    # 第四个或为设备列表更新。触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    

    拔出流程:

    # 第一个或为电平变化。USB 设备拔出,线路短路引发电平变化,有主控器识别,判断设备变化,触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    
    # 第二个或为设备销毁。由主控器发送给目标设备,目标设备未响应,由主控器移除盘符,确认已删除设备或媒体片段,触发 DBT_DEVICEREMOVECOMPLETE 广播。
    wparam:|DBT_DEVICEREMOVECOMPLETE:0x8004|
    lparam:|0xa27b5ef200|
    drive_letter:|5|
    ch( ord('A') + drive_letter):|F|
    
    # 第三个或为设备列表更新。触发 DBT_DEVNODES_CHANGED 广播。
    wparam:|DBT_DEVNODES_CHANGED:0x7|
    lparam:|0x0|
    

    参考

    Python 代码

    所指皆为 Windows 平台。

    基本上关键代码引用于 buskill-app -> buskill.py

    引用

    import win32api
    import win32con
    import win32gui
    import time
    from ctypes import *
    

    常量

    # Device change events (WM_DEVICECHANGE wParam)
    # 设备改变事件
    # 除 0x8000 , 0x8001 和 0x8004 ,其他标识符一般都与 0x0007 相关
    DBT_DEVNODES_CHANGED = 0x0007
    DBT_DEVICEARRIVAL = 0x8000
    DBT_DEVICEQUERYREMOVE = 0x8001
    DBT_DEVICEQUERYREMOVEFAILED = 0x8002
    DBT_DEVICEMOVEPENDING = 0x8003
    DBT_DEVICEREMOVECOMPLETE = 0x8004
    DBT_DEVICETYPESSPECIFIC = 0x8005
    DBT_CONFIGCHANGED = 0x0018
    
    WORD = c_ushort
    DWORD = c_ulong
    
    # 本节参考 1
    class DEV_BROADCAST_HDR(Structure):
    	_fields_ = [
    		("dbch_size", DWORD),
    		("dbch_devicetype", DWORD),
    		("dbch_reserved", DWORD)
    	]
        
    # 本节参考 2
    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.hotplugCallbackWin
    		}
    		
    		# 通过windows窗口类监听 USB 设备插入、删除等操作
    		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
    		)
    
    	# this is a callback function that is registered to be called when a usb
    	# hotplug event occurs in windows
    	# 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
    	def hotplugCallbackWin(self, hwnd, message, wparam, lparam):
    
    		dev_broadcast_hdr = DEV_BROADCAST_HDR.from_address(lparam)
    
    		# USB 存储设备插入
    		if wparam == DBT_DEVICEARRIVAL:
    			loggerUSBInfo(self, hwnd, message, wparam, lparam)
    
    		# USB 存储设备拔出
    		elif wparam == DBT_DEVICEREMOVECOMPLETE:
    			loggerUSBInfo(self, hwnd, message, wparam, lparam)
    		
    		elif wparam == DBT_DEVICEQUERYREMOVE:
    			loggerUSBInfo(self, hwnd, message, wparam, lparam)
    		
    		# 设备更新相关标识符
    		# elif wparam == DBT_DEVNODES_CHANGED or DBT_DEVICEQUERYREMOVEFAILED or DBT_DEVICEMOVEPENDING  or DBT_DEVICETYPESSPECIFIC or DBT_CONFIGCHANGED:
    		# 	loggerUSBInfo(self, hwnd, message, wparam, lparam, 1)
    			
    		else:
    			loggerUSBInfo(self, hwnd, message, wparam, lparam, 1)
    			pass
    
    		return 1
    

    信息输出方法

    def loggerUSBInfo(self, hwnd, message, wparam, lparam, handshake: int = 0):
    	""" 
    	打印 USB 信息
    	handshake: 设备添加或删除时的设备更新操作,默认 0 。
    		Value 0 : 非添加或删除操作,计算输出显示 USB 存储设备盘符。
    		Value 1 : 添加或删除操作,不计算输出显示 USB 存储设备盘符。
    	"""
    
    	# 时间
    	msg = "time:|" + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + "|"
    	loggerPrint(msg, 0, 1)
    
    	# 句柄
    	msg = "hwnd:|" + str(hex(hwnd)) + "|"
    	loggerPrint(msg, 1, 1)
    
    	# 信息
    	msg = "message:|" + str(hex(message)) + "|"
    	loggerPrint(msg, 1, 1)
    
    	# 事件
    	msg = "wparam:|" + WM_DEVICECHANGE.getKey(wparam) + ":" + str(hex(wparam)) + "|"
    	# msg = "wparam:|" + str(wparam) + "|"
    	loggerPrint(msg, 1, 1)
    
    	# Windows 消息
    	msg = "lparam:|" + str(hex(lparam)) + "|"
    	loggerPrint(msg, 1, 1)
    
    	# windows 设备广播数据卷
    	dev_broadcast_volume = DEV_BROADCAST_VOLUME.from_address(lparam)
    	msg = "dev_broadcast_volume:|" + str(dev_broadcast_volume) + "|"
    	loggerPrint(msg, 1, 1)
    
    	# 判断是否有关设备更新操作
    	if handshake == 0:
    		# 驱动器位数
    		drive_letter = drive_from_mask(dev_broadcast_volume.dbcv_unitmask)
    		msg = "drive_letter:|" + str(drive_from_mask(dev_broadcast_volume.dbcv_unitmask)) + "|"
    		loggerPrint(msg, 1, 1)
    
    		# 驱动器盘符
    		msg = "ch( ord('A') + drive_letter):|" + \
    			str(chr(ord('A') + drive_letter)) + '|'
    		loggerPrint(msg, 1, 1)
    
    	print()
    
    def loggerPrint(msg: object, type: int = 0, outFlag: int = 1):
    	"""
    	打印信息
    	type:
    		Value 0 - no logger.
    		Value 1 - debug.
    		Value 2 - error.
    	outFlag: 
    		Value 0 - not print in terminal. 
    		Value 1 - print in terminal.
    	"""
    	
    	if type == 0:
    		pass
    	if type == 1:
    		logger.debug(msg)
    	elif type == 2:
    		logger.error(msg)
    	if outFlag == 1:
    		print(msg)
    

    开始语句

    if __name__ == '__main__':
    	w = Notification()
    	win32gui.PumpMessages()
    

    参考

  • 相关阅读:
    JavaScript return语句 【每日一段代码53】
    JavaScript continue【每日一段代码60】
    JavaScript for 循环【每日一段代码55】
    JavaScript 带有参数并返回值【每日一段代码54】
    JavaScript for 循环标题【每日一段代码56】
    JavaScript 确认按钮 【每日一段代码48】
    JavaScript 参数调用 【每日一段代码51】
    JavaScript while 循环【每日一段代码57】
    SRM 548 DIV2
    POJ 1131 Doing Windows
  • 原文地址:https://www.cnblogs.com/Yogile/p/16389310.html
Copyright © 2020-2023  润新知