Обнаружение вставки мультимедиа в Windows в Python

Мне нужна программа, которая обнаруживает вставку носителя, а также сообщает мне букву диска, чтобы я мог использовать ее и добавлять другие функции, которые будут запускаться при запуске события вставки устройства.

Я думаю, это можно сделать с помощью WMI, используя класс Win32_VolumeChangeEvent. (я нашел некоторые реализации на Powershell и C#, но хочу сделать это на Python). Я знаю, что в конечном итоге также есть модуль wmi для Python, и я нашел это фрагмент кода из списка рассылки Python, но, похоже, он не работает.

Затем я также нашел этот скрипт Python, который мог делать то, что мне нужно . Кажется, это было написано для python 2, и я поправил скобки для функции print(), чтобы она работала на python 3, кроме того, я заметил, что в коде было несколько ненужных ;. (возможно, он был портирован с 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()

Windows отправляет всем окнам верхнего уровня набор сообщений WM_DEVICECHANGE по умолчанию, когда новые устройства или носители (такие как CD или DVD) добавляются и становятся доступными, а также когда существующие устройства или носители удаляются.

Каждое сообщение WM_DEVICECHANGE имеет связанное событие, описывающее изменение, и структуру, предоставляющую подробную информацию об изменении. Структура состоит из заголовка, не зависящего от события, DEV_BROADCAST_HDR, за которым следуют члены, зависящие от события. Члены, зависящие от события, описывают устройство, к которому относится событие. Чтобы использовать эту структуру, приложения должны сначала определить тип события и тип устройства. Затем они могут использовать правильную структуру для выполнения соответствующих действий.

Когда пользователь вставляет новый CD или DVD в дисковод, приложения получают сообщение WM_DEVICECHANGE с событием DBT_DEVICEARRIVAL. Приложение должно проверить событие, чтобы убедиться, что тип поступающего устройства является томом (элемент dbch_devicetypeDBT_DEVTYP_VOLUME) и что изменение влияет на носитель ( dbcv_flagsDBTF_MEDIA).

Здесь вы можете найти реализация на C++ непосредственно из Microsoft MSDN.


ПРОБЛЕМЫ:

Код компилируется без ошибок, и если я вставляю 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_broadcast_volume.dbcv_flags, и оно 0. Затем я попытался напечатать значение DBTF_MEDIA, и оно равно 1. Я вижу, что в коде есть оператор if с побитовой операцией:

if dev_broadcast_volume.dbcv_flags & DBTF_MEDIA:

Если бы оба dev_broadcast_volume.dbcv_flags и DBTF_MEDIA были == 1, побитовая операция вернула бы 1, поэтому if будет True, и код внутри будет выполнен, но dev_broadcast_volume.dbcv_flags == 0, поэтому побитовая операция вернет 0, а оператор if равно False, и код не будет выполнен, верно?

Я попытался полностью удалить оператор if, и хотя проверки больше не существует (она необходима?), теперь буква диска печатается правильно.

Это вывод программы, которую я получаю сейчас:

Something's arrived
It's a volume!
in drive K

person Fabio    schedule 31.07.2016    source источник
comment
dbcv_flags я могу сказать, прочитав, что этот флаг принимает два значения DBTF_NET/DBTF_MEDIA, поэтому «если dev_broadcast_volume.dbcv_flags & DBTF_MEDIA: можете ли вы указать мне, какое условие вы проверяете в этом if предположим, для равенства вы пробовали это ifdev_broadcast_volume.dbcv_flags==DBTF_MEDIA:   -  person RaGa__M    schedule 01.08.2016
comment
Я попробовал после вашего предложения, но, похоже, это все еще не работает   -  person Fabio    schedule 01.08.2016
comment
USB-диск монтируется как диск, а не как съемный носитель, поэтому в этом случае нет необходимости проверять наличие DBTF_MEDIA.   -  person Eryk Sun    schedule 01.08.2016
comment
Обратите внимание, что когда диски монтируются в папку (например, C:\Mount\SomeDiskName), система не генерирует сообщения DBT_DEVTYP_VOLUME. Вместо этого требуется вызов RegisterDeviceNotification для класса устройств GUID_DEVINTERFACE_VOLUME. Вы по-прежнему получаете сообщение WM_DEVICECHANGE, но для DBT_DEVTYP_DEVICEINTERFACE, у которого есть одна из точек монтирования. Оттуда вы можете вызвать GetVolumeNameForVolumeMountPoint, а затем GetVolumePathNamesForVolumeName, чтобы получить все точки монтирования (буквы дисков и папки).   -  person Eryk Sun    schedule 01.08.2016
comment
Вы когда-нибудь исправляли это?   -  person    schedule 18.09.2018
comment
это ответило на мой вопрос, но его было очень трудно найти в Google. Вот несколько ключевых слов, которые помогут следующему парню: usb hotplugging hotplug callback register Registration Events windows python libusb альтернатива stackoverflow.com/questions/62601721/   -  person Michael Altfield    schedule 27.06.2020