在Python中检测Windows上的媒体插入
我需要一个程序来检测媒体插入,并告诉我驱动器号,这样我就可以在此基础上进行构建,并添加其他功能,以便在触发设备插入事件时运行 我认为可以使用WMI使用(我在Powershell和C中找到了一些实现,但我想用Python实现)。我知道python最终也有了在Python中检测Windows上的媒体插入,python,windows,usb-drive,Python,Windows,Usb Drive,我需要一个程序来检测媒体插入,并告诉我驱动器号,这样我就可以在此基础上进行构建,并添加其他功能,以便在触发设备插入事件时运行 我认为可以使用WMI使用(我在Powershell和C中找到了一些实现,但我想用Python实现)。我知道python最终也有了wmi模块,我从python邮件列表中找到了一段代码,但它似乎不起作用 然后我还找到了Python脚本,可以做我需要的事情。它似乎是为Python2编写的,我调整了print()函数的括号以使其在Python3上工作,此外,我注意到还有一些不必要
wmi
模块,我从python邮件列表中找到了一段代码,但它似乎不起作用
然后我还找到了Python脚本,可以做我需要的事情。它似乎是为Python2编写的,我调整了print()函数的括号以使其在Python3上工作,此外,我注意到还有一些不必要的代码>在代码中。(可能是从C移植过来的,开发人员把它们错放在那里了。这个python脚本使用ctypes)
我向您展示我获得的代码:
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()
添加新设备或媒体(如CD或DVD)并使其可用时,以及删除现有设备或媒体时,Windows会向所有顶级窗口发送一组默认的WM_DEVICECHANGE消息
每个WM_DEVICECHANGE消息都有一个描述更改的关联事件,以及一个提供更改详细信息的结构。该结构包含一个独立于事件的头,DEV\u BROADCAST\u HDR,后跟事件相关的成员。事件相关成员描述事件应用到的设备。要使用此结构,应用程序必须首先确定事件类型和设备类型。然后,他们可以使用正确的结构采取适当的行动
当用户将新的CD或DVD插入驱动器时,应用程序将收到一条带有DBT\U DEVICEARRIVAL事件的WM\U DEVICECHANGE消息。应用程序必须检查事件,以确保到达的设备类型为卷(dbch_设备类型成员为DBT_DEVTYP_卷),并且更改会影响介质(dbcv_标志成员为DBTF_介质)
可以直接从微软MSDN ./P>中找到C++的实现
问题:
代码编译时没有错误,如果插入USB驱动器,我会正确地收到消息“有东西到了”和“这是一个卷!”,但消息“有一些媒体”和驱动器号从未显示,因此这部分代码好像不起作用:
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))
我需要修复程序,以便了解插入的新介质的驱动器号
更新:
我试图打印dev\u broadcast\u volume.dbcv\u flags的值,它是0。
然后我尝试打印DBTF_MEDIA的值,它是1。
我看到在代码中有一个带有按位操作的if语句:
if dev_broadcast_volume.dbcv_flags & DBTF_MEDIA:
如果dev_broadcast_volume.dbcv_标志和DBTF_MEDIA均为==1,则按位操作将返回1,因此if语句将是True,其中的代码将被执行,但dev_broadcast\u volume.dbcv_flags==0,因此按位操作将返回0,if语句将是False,代码将不会被执行,对吗
我试图完全删除if语句,尽管检查已不存在(是否有必要?),但现在驱动器号已正确打印
这是我现在得到的程序的输出:
Something's arrived
It's a volume!
in drive K
dbcv\u flags
我可以从阅读中说,这个标志有两个值DBTF\u NET/DBTF\u MEDIA
so'if dev\u broadcast\u volume.dbcv\u flags&DBTF\u MEDIA:
你能告诉我你在这个中检查的是什么条件吗假设相等吗ifdev\u broadcast\u volume.dbcv\u flags==DBTF\u MEDIA:
i按照您的建议尝试,但似乎仍然不起作用USB磁盘作为驱动器装载,而不是作为可移动介质装载,因此在这种情况下没有理由检查DBTF_介质
。请注意,当驱动器装载在文件夹中(例如C:\Mount\SomeDiskName
)时,系统不会生成DBT_DEVTYP_卷
消息。相反,它需要为GUID\u deviceinterface\u VOLUME
设备类调用RegisterDeviceNotification
。您仍然会收到WM\u DEVICECHANGE
消息,但对于DBT\u DEVTYP\u DEVICEINTERFACE
,它有一个装入点。从那里,您可以调用GetVolumeNameForVolumeMountPoint
,然后调用GetVolumePathNamesForVolumeName
,以获取所有装载点(驱动器号和文件夹)。您解决过这个问题吗?