Страница 1 из 1

Отслеживание подключения/отключения устройств

Добавлено: 22 фев 2008, 14:02
Kankay
Как можно отследить подключение/отключение устройств (Flash, дисов в CD/DVD приводах и т.д.) ?

Почитав всякого, я написал описанный ниже способ. Но там возникла ситуация сродни: Картина Репина приплыли - 3 дня гребли, а трос отвязать забыли. У меня возник вопрос с GUID (смотри ниже).
Так же не хочется использовать способы в которых надо рыться в реестре, отслеживания по таймеру и т.д.

Кстати, я часто видел типа "ответы" по данной тематике, но в них почему-то дальше фразы о том, что надо использовать сообщение WM_DEVICECHANGE тема не развивается... :?

Вот один из возможных способ ( но может есть другие? ) :

Когда происходят изменения в устройствах (например, подключение/отключение Flash, дисов в CD/DVD приводах и т.д.) система рассылает всем открытым в данный момент приложениям сообщение WM_DEVICECHANGE. Сейчас интересует случай когда происходит подключение/отключение Flash, дисов в CD/DVD приводах.
Чтобы приложение основанное на MFC могло отловить и отроботать данное сообщение в карте сообщений необходимо написать следующее:

Код: Выделить всё

ON_MESSAGE(WM_DEVICECHANGE, OnDeviceChange)


Из значений которые может принимать wParam для интересующего нас случая важно следующее:
DBT_DEVICEARRIVAL – подключили новое устройство
DBT_DEVICEREMOVECOMPLETE – устройство было отключено
Как я понял lParam является указателем на структуру, которая содержит в себе информацию о произошедших изменениях с устройствами.
Функция OnDeviceChange реализуется следующим образом:

Код: Выделить всё

#include <dbt.h> 

LRESULT OnDeviceChange(WPARAM wParam, LPARAM lParam) 
{ 
  // следует обратить внимание на эту строку: 
  PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam; 

  switch(wParam) 
  { 
      case DBT_DEVICEARRIVAL: 
       if (lpdb -> dbch_devicetype == DBT_DEVTYP_VOLUME) 
       { 
           PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb; 

            if (lpdbv -> dbcv_flags & DBTF_MEDIA) 
               { 
                  MessageBox (NULL, _T(“Устройство подключено”),  _T("WM_DEVICECHANGE"), MB_OK ); 
               } 
         } 
         break; 

      case DBT_DEVICEREMOVECOMPLETE: 
         if (lpdb -> dbch_devicetype == DBT_DEVTYP_VOLUME) 
         { 
             PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb; 

             if (lpdbv -> dbcv_flags & DBTF_MEDIA) 
               { 
                  MessageBox (NULL, _T(“Устройство отключено”),  _T("WM_DEVICECHANGE"), MB_OK ); 
               } 
         } 
         break; 
   } 
} 
Возможность использовать сообщение DBT_DEVNODES_CHANGED не подходит т.к. оно приходит по нескольку раз после подключения/отключения устройства.

И тут начинается самое интерестное:

По умолчанию сообщение WM_DEVICECHANGE, как уже было выше написано, рассылается всем открытым приложениям, но приложение не всегда будет принимать значения параметров wParam и lParam :( (По-умолчанию они приходят top-level окнам). Т.о. если приложение оконное и не top-level или является сервисом, то для того что бы wParam и lParam содержали нужную информацию (DBT_...) необходимо зарегестрироваться в системе для получения сообщения. Это делается при помощи функции RegisterDeviceNotification:

Код: Выделить всё

HDEVNOTIFY RegisterDeviceNotification 
   ( 
       HANDLE hRecipient, 
       LPVOID NotificationFilter, 
       DWORD Flags 
   ); 


hRecipient – хэндл окна или сервиса, который регистрируется в системе для получения сообщения.

Flags – определяет кто именно регистрируется сервис или приложение:
DEVICE_NOTIFY_WINDOW_HANDLE – hRecipient является хэндлом окна
DEVICE_NOTIFY_SERVICE_HANDLE – hRecipient является хэндлом сервиса

NotificationFilter – указатель на блок данных который содержит некоторую информацию об устройстве от которого принято сообщение ( :confused: ) :

DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;

одно из полей этой структуры - dbcc_classguid - переменная типа GUID

Скажите, пожалуйста, что это (GUID) и как это получить в данном случае? И вообще, где правильно регистрироваться для получения сообщений?