在 Windows 上检测插入的 USB
Posted
技术标签:
【中文标题】在 Windows 上检测插入的 USB【英文标题】:Detect inserted USB on Windows 【发布时间】:2011-05-15 11:13:13 【问题描述】:我目前正在用 python 编写一个安全工具,它作为守护程序在主机上运行。每当检测到 USB 存储设备时,它会将所有文件从 USB 复制到主机上的某个目录。有没有简单的方法来做这种 USB 检测/接口?提前致谢!
【问题讨论】:
您使用的是什么操作系统?对于linux,可以使用dbus:redclay.altervista.org/wiki/doku.php?id=projects:hal-automount 对于 linux,另见***.com/questions/469243/… 感谢您的回复!我目前正在运行 Windows。我是否必须使用 WinAPI 来完成此操作?再次感谢! 【参考方案1】:是的,您需要使用RegisterDeviceNotification
Windows API 调用。据我所知,没有封装这个功能的Python模块,所以你必须使用ctypes
来调用这个函数。
幸运的是,您不是第一个想要这样做的人,所以网上流传着一些代码示例。 WxPython 提供了一个code sample,但是当您正在编写一个守护进程时,您可能对此不感兴趣。你可能想试试下面的代码示例,它同时依赖于ctypes
和pywin32
,从Tim Golden 中无耻地提升:
import win32serviceutil
import win32service
import win32event
import servicemanager
import win32gui
import win32gui_struct
struct = win32gui_struct.struct
pywintypes = win32gui_struct.pywintypes
import win32con
GUID_DEVINTERFACE_USB_DEVICE = "A5DCBF10-6530-11D2-901F-00C04FB951ED"
DBT_DEVICEARRIVAL = 0x8000
DBT_DEVICEREMOVECOMPLETE = 0x8004
import ctypes
#
# Cut-down clone of UnpackDEV_BROADCAST from win32gui_struct, to be
# used for monkey-patching said module with correct handling
# of the "name" param of DBT_DEVTYPE_DEVICEINTERFACE
#
def _UnpackDEV_BROADCAST (lparam):
if lparam == 0: return None
hdr_format = "iii"
hdr_size = struct.calcsize (hdr_format)
hdr_buf = win32gui.PyGetMemory (lparam, hdr_size)
size, devtype, reserved = struct.unpack ("iii", hdr_buf)
# Due to x64 alignment issues, we need to use the full format string over
# the entire buffer. ie, on x64:
# calcsize('iiiP') != calcsize('iii')+calcsize('P')
buf = win32gui.PyGetMemory (lparam, size)
extra =
if devtype == win32con.DBT_DEVTYP_DEVICEINTERFACE:
fmt = hdr_format + "16s"
_, _, _, guid_bytes = struct.unpack (fmt, buf[:struct.calcsize(fmt)])
extra['classguid'] = pywintypes.IID (guid_bytes, True)
extra['name'] = ctypes.wstring_at (lparam + struct.calcsize(fmt))
else:
raise NotImplementedError("unknown device type %d" % (devtype,))
return win32gui_struct.DEV_BROADCAST_INFO(devtype, **extra)
win32gui_struct.UnpackDEV_BROADCAST = _UnpackDEV_BROADCAST
class DeviceEventService (win32serviceutil.ServiceFramework):
_svc_name_ = "DevEventHandler"
_svc_display_name_ = "Device Event Handler"
_svc_description_ = "Handle device notification events"
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__ (self, args)
self.hWaitStop = win32event.CreateEvent (None, 0, 0, None)
#
# Specify that we're interested in device interface
# events for USB devices
#
filter = win32gui_struct.PackDEV_BROADCAST_DEVICEINTERFACE (
GUID_DEVINTERFACE_USB_DEVICE
)
self.hDevNotify = win32gui.RegisterDeviceNotification (
self.ssh, # copy of the service status handle
filter,
win32con.DEVICE_NOTIFY_SERVICE_HANDLE
)
#
# Add to the list of controls already handled by the underlying
# ServiceFramework class. We're only interested in device events
#
def GetAcceptedControls(self):
rc = win32serviceutil.ServiceFramework.GetAcceptedControls (self)
rc |= win32service.SERVICE_CONTROL_DEVICEEVENT
return rc
#
# Handle non-standard service events (including our device broadcasts)
# by logging to the Application event log
#
def SvcOtherEx(self, control, event_type, data):
if control == win32service.SERVICE_CONTROL_DEVICEEVENT:
info = win32gui_struct.UnpackDEV_BROADCAST(data)
#
# This is the key bit here where you'll presumably
# do something other than log the event. Perhaps pulse
# a named event or write to a secure pipe etc. etc.
#
if event_type == DBT_DEVICEARRIVAL:
servicemanager.LogMsg (
servicemanager.EVENTLOG_INFORMATION_TYPE,
0xF000,
("Device %s arrived" % info.name, '')
)
elif event_type == DBT_DEVICEREMOVECOMPLETE:
servicemanager.LogMsg (
servicemanager.EVENTLOG_INFORMATION_TYPE,
0xF000,
("Device %s removed" % info.name, '')
)
#
# Standard stuff for stopping and running service; nothing
# specific to device notifications
#
def SvcStop(self):
self.ReportServiceStatus (win32service.SERVICE_STOP_PENDING)
win32event.SetEvent (self.hWaitStop)
def SvcDoRun(self):
win32event.WaitForSingleObject (self.hWaitStop, win32event.INFINITE)
servicemanager.LogMsg (
servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STOPPED,
(self._svc_name_, '')
)
if __name__=='__main__':
win32serviceutil.HandleCommandLine (DeviceEventService)
【讨论】:
在Windows 10中使用“start”参数执行脚本时,控制台中会出现以下消息:“正在启动服务DevEventHandler”、“启动服务时出错:访问被拒绝”【参考方案2】:好的,有一种更简单的方法可以在 Windows 机器上找到 USB 设备,使用以下代码:
import win32file
def locate_usb():
drive_list = []
drivebits = win32file.GetLogicalDrives()
for d in range(1, 26):
mask = 1 << d
if drivebits & mask:
# here if the drive is at least there
drname = '%c:\\' % chr(ord('A') + d)
t = win32file.GetDriveType(drname)
if t == win32file.DRIVE_REMOVABLE:
drive_list.append(drname)
return drive_list
代码实际上取自https://mail.python.org/pipermail/python-win32/2006-December/005406.html
【讨论】:
不知道这有多可靠,但我想对于许多用例来说这已经足够了。感谢您提供简单的示例。 另见底层窗口fileapi函数GetLogicalDrives和GetDriveTypeA(和A vs W) 效果很好。此实现跳过了驱动器'A:\'
(位位置0),但如果我们假设驱动器'A:\'
永远不可移动,这不是问题。可以使用 while mask < drivebits: ...
而不是遍历所有 26 个字符。以上是关于在 Windows 上检测插入的 USB的主要内容,如果未能解决你的问题,请参考以下文章