OberonCore

Библиотека  Wiki  Форум  BlackBox  Компоненты  Проекты
Текущее время: Понедельник, 20 Октябрь, 2025 22:36

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 3 ] 
Автор Сообщение
СообщениеДобавлено: Пятница, 17 Октябрь, 2025 03:50 

Зарегистрирован: Воскресенье, 06 Август, 2017 19:33
Сообщения: 98
Для получения полной информации о съёмных накопителях в Windows необходимо зарегистрировать устройство при помощи RegisterDeviceNotificationW. Но у меня никак не получается это сделать: выдаёт ошибку "Ошибка регистрации уведомлений", а "информативный" GetLastError как всегда выдаёт 0. Я колдовал по-всякому и DeepSeek'а спрашивал - не даёт вразумительного ответа.

У меня Windows 11. Вот коды модулей:
Код:
MODULE MyRegDev ["user32"];
   PROCEDURE RegisterDeviceNotificationW* (hRecipient, NotificationFilter, Flags: INTEGER): INTEGER;
   PROCEDURE UnregisterDeviceNotification* (handle: INTEGER): INTEGER;
END MyRegDev.


И основной:
Код:
MODULE UsbNotifier;

IMPORT SYSTEM, WinApi, HostWindows, Windows, StdLog, MyRegDev;

CONST
   WM_DEVICECHANGE = 219H; (* Сообщение об изменении устройств *)
   DBT_DEVICEARRIVAL = 8000H; (* Устройство подключено *)
   DBT_DEVICEREMOVECOMPLETE = 8004H; (* Устройство отключено *)
   DBT_DEVTYP_VOLUME = 2H; (* Тип устройства - том (флешка) *)

TYPE

   DEV_BROADCAST_HDR1* = RECORD
      dbch_size*: INTEGER;
      dbch_devicetype*: INTEGER;
      dbch_reserved*: INTEGER;
   END;

   DEV_BROADCAST_HDR* = POINTER TO DEV_BROADCAST_HDR1;

   DEV_BROADCAST_VOLUME* = POINTER TO RECORD
      dbch_size*: INTEGER;
      dbch_devicetype*: INTEGER;
      dbch_reserved*: INTEGER;
      dbcv_unitmask*: SET;
      dbcv_flags*: SHORTINT;
   END;

VAR
   hwndMain: INTEGER;
   registered: WinApi.BOOL;
   devNotify: INTEGER;

(* Callback-процедура для обработки оконных сообщений *)
PROCEDURE [callback] MainWinProc(hwnd: INTEGER; msg: INTEGER; wParam: INTEGER; lParam: INTEGER): INTEGER;
VAR
   devHdr: DEV_BROADCAST_HDR;
   devVolume: DEV_BROADCAST_VOLUME;
   driveLetter: CHAR;
   driveMask: SET;
   i: INTEGER;
BEGIN
   IF msg = WM_DEVICECHANGE THEN
      (* Проверяем, что устройство подключено *)
      IF wParam = DBT_DEVICEARRIVAL THEN
         devHdr := SYSTEM.VAL(DEV_BROADCAST_HDR, lParam);
         IF devHdr # NIL THEN
            (* Проверяем, что это том (флешка) *)
            IF devHdr.dbch_devicetype = DBT_DEVTYP_VOLUME THEN
               devVolume := SYSTEM.VAL(DEV_BROADCAST_VOLUME, lParam);;
               driveMask := devVolume.dbcv_unitmask;
               
               (* Определяем букву подключенного диска *)
               i := 0;
               WHILE (i < 26) & ~(i IN driveMask) DO
                  INC(i)
               END;
               IF i < 26 THEN
                  (* Выводим сообщение о подключении флешки *)
                  StdLog.String ("Флешка подключена на диск: "); StdLog.Char(CHR(ORD("A") + i)); StdLog.Ln
               END
            END
         END
      (* Проверяем, что устройство отключено *)
      ELSIF wParam = DBT_DEVICEREMOVECOMPLETE THEN
         devHdr := SYSTEM.VAL(DEV_BROADCAST_HDR, lParam);
         IF (devHdr # NIL) & (devHdr.dbch_devicetype = DBT_DEVTYP_VOLUME) THEN
            StdLog.String ("Флешка отключена"); StdLog.Ln
         END
      END
   END;
   
   (* Передаем сообщение на стандартную обработку *)
   RETURN WinApi.DefWindowProc(hwnd, msg, wParam, lParam)
END MainWinProc;

(* Процедура создания окна *)
PROCEDURE CreateMainWindow*;
VAR
   wc: WinApi.WNDCLASS;
   className: WinApi.PtrSTR;
   style: SET;
BEGIN
   IF hwndMain = 0 THEN
      
      (* Заполняем структуру класса окна *)
      className := "USBNotifierWindow";
      wc.style := {};
      wc.lpfnWndProc := MainWinProc;
      wc.cbClsExtra := 0;
      wc.cbWndExtra := 0;
      wc.hInstance := WinApi.GetModuleHandle(NIL);
      wc.hIcon := 0;
      wc.hCursor := 0;
      wc.hbrBackground := 0;
      wc.lpszMenuName := NIL;
      wc.lpszClassName := className;
      
      (* Регистрируем класс окна *)
      IF WinApi.RegisterClass(wc) = WinApi.FALSE THEN
         StdLog.String("Ошибка регистрации класса окна"); StdLog.Int(WinApi.GetLastError()); StdLog.Ln
      ELSE
         (*Создаем невидимое окно *)
         style := WinApi.WS_OVERLAPPEDWINDOW;
         hwndMain := WinApi.CreateWindowEx({}, className, "USB Notifier", style, WinApi.CW_USEDEFAULT, WinApi.CW_USEDEFAULT, 300, 200, 0, 0, wc.hInstance, 0);
         
         IF hwndMain = WinApi.FALSE THEN
            StdLog.String("Ошибка создания окна"); StdLog.Ln
         ELSE
            (* Делаем окно невидимым *)
            registered := WinApi.ShowWindow(hwndMain, WinApi.SW_HIDE);
         END
      END
   END
END CreateMainWindow;

(* Процедура уничтожения окна *)
PROCEDURE DestroyMainWindow*;
VAR result: WinApi.BOOL;
BEGIN
   IF hwndMain # 0 THEN
      IF WinApi.DestroyWindow(hwndMain) = WinApi.FALSE THEN
         StdLog.String ("Не удалось удалить окно"); StdLog.Ln
      ELSE
         IF WinApi.UnregisterClassW ("USBNotifierWindow", WinApi.GetModuleHandle(NIL)) = WinApi.FALSE THEN
            StdLog.String ("Не удалось удалить класс"); StdLog.Ln
         ELSE
            IF (devNotify # 0) & (MyRegDev.UnregisterDeviceNotification (devNotify) = WinApi.FALSE) THEN StdLog.String ("Не удалось отменить регистрацию."); StdLog.Ln END
         END;
         hwndMain := 0;
         registered := WinApi.FALSE;
      END
   END
END DestroyMainWindow;

(* Процедура регистрации для получения уведомлений об устройствах *)
PROCEDURE RegisterForDeviceNotifications*;
CONST
   DEVICE_NOTIFY_WINDOW_HANDLE = 0;
VAR
   filter: DEV_BROADCAST_HDR;
   
BEGIN
   IF hwndMain = WinApi.FALSE THEN
      CreateMainWindow()
   ELSE
      (*DestroyMainWindow*)
   END;
   
   IF hwndMain # 0 THEN
      (* Заполняем структуру фильтра *)
      NEW(filter);
      filter.dbch_size := SIZE(DEV_BROADCAST_HDR1);
      StdLog.Int(filter.dbch_size);StdLog.Ln;
      filter.dbch_devicetype := DBT_DEVTYP_VOLUME;
      filter.dbch_reserved := 0;
      
      (* Регистрируем для получения уведомлений *)
      devNotify := MyRegDev.RegisterDeviceNotificationW(hwndMain, SYSTEM.ADR(filter), DEVICE_NOTIFY_WINDOW_HANDLE);
      IF devNotify = WinApi.FALSE THEN
         StdLog.String("Ошибка регистрации уведомлений:"); StdLog.Int(WinApi.GetLastError()); StdLog.Ln;
         (*DestroyMainWindow*)
      ELSE
         StdLog.String("Регистрация для уведомлений USB выполнена успешно");
      END
   END
END RegisterForDeviceNotifications;

(* Инициализация модуля *)
BEGIN
   hwndMain := 0;
   registered := WinApi.FALSE;
END UsbNotifier.

UsbNotifier.RegisterForDeviceNotifications

UsbNotifier.DestroyMainWindow


Окно регистрируется: при подключении флешки выдаёт сообщение: "Флешка подключена на диск" с указанием буквы диска.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Пятница, 17 Октябрь, 2025 18:40 
Аватара пользователя

Зарегистрирован: Вторник, 28 Август, 2007 00:55
Сообщения: 603
Откуда: Украина, Днепропетровская обл.
Для получения уведомлений о подключении/отключении съёмных накопителей (томов, 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


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Суббота, 18 Октябрь, 2025 16:55 

Зарегистрирован: Воскресенье, 06 Август, 2017 19:33
Сообщения: 98
Огромное спасибо за подробный ответ, а то я замучился с этой регистрацией. Оказывается ларчик просто открывался. Ещё раз спасибо!


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 3 ] 

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 4


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Вся информация, размещаемая участниками на конференции (тексты сообщений, вложения и пр.) © 2005-2025, участники конференции «OberonCore», если специально не оговорено иное.
Администрация не несет ответственности за мнения, стиль и достоверность высказываний участников, равно как и за безопасность материалов, предоставляемых участниками во вложениях.
Без разрешения участников и ссылки на конференцию «OberonCore» любое воспроизведение и/или копирование высказываний полностью и/или по частям запрещено.
Powered by phpBB® Forum Software © phpBB Group
Русская поддержка phpBB