Для получения уведомлений о подключении/отключении съёмных накопителей (томов, volumes) в Windows не требуется использовать RegisterDeviceNotificationW с типом DBT_DEVTYP_VOLUME. Эти события (в частности, DBT_DEVICEARRIVAL и DBT_DEVICEREMOVECOMPLETE для томов) транслируются (broadcast) всем топ-левел окнам автоматически, без необходимости регистрации. Именно поэтому в Вашем коде определение флешки работает, несмотря на "ошибку" регистрации.
Функция RegisterDeviceNotificationW не поддерживает DBT_DEVTYP_VOLUME в качестве фильтра — она предназначена для других типов устройств, таких как интерфейсы устройств (DBT_DEVTYP_DEVICEINTERFACE) или OEM-устройства. Если передать ей структуру с dbch_devicetype = DBT_DEVTYP_VOLUME, она всегда вернёт NULL (FAIL), независимо от других параметров. Это задокументированное поведение: функция просто не предназначена для этого случая. Отсутствие ошибки в GetLastError (возврат 0) может быть связано с тем, что функция не устанавливает код ошибки для неподдерживаемого типа (или это артефакт вызова).
Ваш код в целом правильный для обработки сообщений, и регистрация здесь избыточна. Чтобы избавиться от "ошибки", просто удалите часть с RegisterDeviceNotificationW — окно и так получит WM_DEVICECHANGE. Если хотите оставить попытку регистрации для других целей, обработайте неудачу молча, но это не повлияет на функциональность.
Дополнительные замечания по коду
Хотя основная проблема в неподдержке типа, в Вашем коде есть несколько потенциальных ошибок, которые могли бы мешать в других сценариях:
Неправильный тип и размер структуры: Вы используете DEV_BROADCAST_HDR (размер ~12 байт), но для томов нужна полная DEV_BROADCAST_VOLUME (размер ~18 байт в Вашей типизации: 4+4+4+4+2). Измените:
Код:
VAR filter: DEV_BROADCAST_VOLUME;
Затем:
Код:
NEW(filter);
filter.dbch_size := SIZE(DEV_BROADCAST_VOLUME1); (* Или SIZE(filter^), в зависимости от системы *)
filter.dbch_devicetype := DBT_DEVTYP_VOLUME;
filter.dbch_reserved := 0;
filter.dbcv_unitmask := {0..25}; (* Маска для всех дисков A-Z (0x03FFFFFF) *)
filter.dbcv_flags := 0; (* Или 1 (DBTF_MEDIA) для фокуса на съёмных носителях *)
Но, как сказано, это не поможет — функция всё равно откажет.
Неправильная передача адреса: SYSTEM.ADR(filter) возвращает адрес переменной filter (указателя), а не адрес структуры. Нужно передать адрес самой структуры:
Код:
oberondevNotify := MyRegDev.RegisterDeviceNotificationW(hwndMain, SYSTEM.VAL(INTEGER, filter), DEVICE_NOTIFY_WINDOW_HANDLE);
(Предполагается, что INTEGER совместим с адресами в Вашей среде)
Другие мелочи:
В обработчике: Добавьте проверку на devVolume.dbcv_flags с DBTF_MEDIA (0x1), чтобы фильтровать только съёмные носители (флешки), а не все тома.
Если код для сервиса (без окна), то для получения уведомлений нужен RegisterDeviceNotification с сервис-хендлом и флагом DEVICE_NOTIFY_SERVICE_HANDLE, но у Вас окно, так что ок.
Тестируйте на реальной флешке: Убедитесь, что dbcv_unitmask правильно интерпретируется (SET в Oberon — 32-бит, как DWORD).
Если Вам нужно регистрировать уведомления именно для USB-устройств (не томов), используйте DBT_DEVTYP_DEVICEINTERFACE с GUID класса для USB Mass Storage (например, {53f56307-b6bf-11d0-94f2-00a0c91efb8b} для USB). Структура будет DEV_BROADCAST_DEVICEINTERFACE, и регистрация сработает.
Ваш код работает, как задумано, — просто игнорируйте/уберите неудачную регистрацию.
(c) GROK