简体   繁体   English

USB 在 Windows 上使用 python 热插拔回调

[英]USB Hotplugging callbacks with python on Windows

Is it possible to write a python script such that a function is called whenever a USB device is added or removed on Windows?是否可以编写 python 脚本,以便在 ZAEA234289CE3AA9B4ECDA 上添加或删除 USB 设备时调用 function?

libusb (and corresponding python modules such as libusb1 ) appears to be the most popular solution, but it lacks hotplugging callback registration support in Windows. libusb (以及相应的 python 模块,例如libusb1 )似乎是最流行的解决方案,但它在 Windows 中缺乏热插拔回调注册支持。 A feature request has been open for this since 2015, and it's still not implemented.自 2015 年以来,已为此提出功能请求,但仍未实施。

I've seen some hacks query Windows' usb devices at some interval, do a diff of the current list of devices from the previous list of devices, and use this as an alternative.我已经看到一些黑客在一段时间内查询 Windows 的 usb 设备,对当前设备列表与之前的设备列表进行比较,并将其用作替代方案。 Due to the nature of my application, this hack would be a huge security risk and is not a valid solution.由于我的应用程序的性质,这种 hack 将是一个巨大的安全风险,并且不是一个有效的解决方案。 I need actual callback registration on usb hotplug events.我需要对 usb 热插拔事件进行实际回调注册。

Though less ideal, I'm open to writing something in C or C++ and then writing a python binding to that code, if necessary.虽然不太理想,但如果有必要,我愿意在 C 或 C++ 中编写一些东西,然后编写一个 python 绑定到该代码。

Is it possible to have a python function called when a usb device is connected or disconnected on Windows?当 usb 设备在 ZAEA23489CE3AA9B640EZ3EB4B2 上连接或断开时,是否可以调用 python function?

Yes, you can use ctypes in python to register a callback for WM_DEVICECHANGE messages.是的,您可以使用 python 中的 ctypes 来注册WM_DEVICECHANGE消息的回调。

I was successfully able to add register callback functions to usb hotplug events in both linux (using the libusb1 python module) and windows (using the ctypes python module). I was successfully able to add register callback functions to usb hotplug events in both linux (using the libusb1 python module) and windows (using the ctypes python module). The bulk of the relevant windows code can be found here:大部分相关的 windows 代码可以在这里找到:

Which is largely based on the code found at these links:这主要基于在这些链接中找到的代码:

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()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM