Delphi Робота з пристроями в Windows - все про IT та програмування

Written on 26 Січня 2009 . Posted in Delphi

ЗМІСТ

Отримання списку пристроїв

Другим параметром повинен бути покажчик на буфер, у який буде збережено рядок з ім'ям класу. Третім параметром повинен йти розмір буфера, що передається. Якщо вказаного буфера не вистачить, то необхідний розмір буде збережено в змінній покажчик, яку ми передамо четвертим параметром.

Список класів ми отримали. Тепер нам треба отримати список пристроїв, які належать деякому класу. Тут до нас допоможе функція SetupDiGetClassDevs:

У цій функції багато параметрів опціональні крім останнього. Перший параметр визначає клас пристроїв для перерахування. Якщо цей параметр дорівнює нулю, то перераховуватимуться всі пристрої в системі. Другий і третій параметри (відповідно, ім'я PnP перечислювача і хендл форми) можуть дорівнювати нулю. Останній параметр найважливіший. Він може приймати одне з наступних значень або їх комбінацію:

  • DIGCF_ALLCLASSES Буде повернутий список усіх пристроїв та всіх класів, встановлених на даний момент у системі. Перший параметр буде проігноровано.
  • DIGCF_DEVICEINTERFACE Повернення до списку пристроїв, які підтримують інтерфейси.
  • DIGCF_DEFAULT Повернення до списку пристроїв, які асоціюються з системою за промовчанням.
  • DIGCF_PRESENT Буде повернутий список пристроїв, які зараз присутні в системі.
  • DIGCF_PROFILE Повернено список пристроїв, які є частиною поточного апаратного профілю.

У нашому випадку треба вказати лише клас пристроїв та вказати останнім параметром прапор DIGCF_PRESENT. При успішному виклику функція повертає хендлодержаного списку.

Отже, у нас є список (вірніше його хендл) і нам треба якось перерахувати всі пристрої, що знаходяться в ньому. На допомогу нам прийде функція під назвою SetupDiEnumDeviceInfo:

З першим параметром, я гадаю, все ясно. Другий парметр задає індекс у списку. Третій параметр це покажчик на структуру SP_DEVINFO_DATA, в якій буде збережено інформацію про пристрій. Якщо функція повернула значення TRUE, то інформація вилучена успішно, а якщо FALSE, то в більшості випадків це означає, що ми прийшли до кінця списку. Для перерахування всього списку нам треба буде в циклі викликати функцію SetupDiEnumDeviceInfo щоразу збільшуючи значення індексу на одиницю доти, поки не отримаємо негативний результат. Отже, у нас є структура, в якій зберігається інформація про пристрій: , головним полем тут є поле DevInst, яка зберігає хендл пристрою. Щоб отримати ім'я пристрою (або його опис) нам потрібно використовувати функцію SetupDiGetDeviceRegistryProperty. Далі її опис

Другий параметр - це покажчик на структуру SP_DEVINFO_DATA. Третій параметр визначає тип інформації, яку ми хочемо отримати. Для нас важливими є два прапори: SPDRP_FRIENDLYNAME і SPDRP_DEVICEDESC. Далі йде опціональний параметр який задає покажчик на змінну в якій буде збережено тип даних ключа реєстру, з якого було вилучено інформацію. Далі йде ще три параметри які задають відповідно покажчик на буфер для збереження інформації, розмір буфера та розмір реально скопійованих даних у нер. Якщо ми будемо використовувати прапор SPDRP_FRIENDLYNAME, то отримаємо замість моделі жорсткого диска дисковий накопичувач, а при використанні прапора SPDRP_DEVICEDESC ми отримаємо модель жорсткого диска. Не завжди інформація дляобидва параметри представлені, іноді є тільки для SPDRP_FRIENDLYNAME, а іноді є тільки для SPDRP_DEVICEDESC. Якщо при використанні першого прапора ми отримали порожній рядок, треба отримати інформацію з використанням другого прапора.

Наступна функція отримує ім'я пристрою за хендлом перерахування та структурою SP_DEVINFO_DATA.

У результаті у нас вимальовується функція, яка отримує список пристроїв за заданим GUID класу.

Ця функція виводить список пристроїв заданого класу компонент TreeView. Вузол дерева TreeView визначається першим параметром. Тепер ми можемо написати функцію яка і зробить виведення цього списку пристроїв у компонент TreeView. Ось вона:

Спочатку формується список рядків з іменами класів та покажчиків на їх GUID'и. Потім здійснюється виклик попередньої функції кожного класу.

Увімкнення та вимкнення пристроїв

Станом пристрою керує функція SetupDiSetClassInstallParams. Її опис:

З першими двома параметрами я думаю все ясно. Третій параметр визначає покажчик на структуру SP_CLASSINSTALL_HEADER. Четвертий параметр визначає розмір третього параметра. За допомогою цієї функції можна робити різні дії з пристроями та, зрозуміло, для кожної дії використовуються різні структури. Але у кожної зі структур перша складова однакова – структура SP_CLASSINSTALL_HEADER, ось вона:

Поле InstallFunction задає операцію, що виробляється над пристроєм. Для ввімкнення/вимкнення це поле дорівнює константі DIF_PROPERTYCHANGE. Для увімкнення/вимкнення пристрою використовується наступна структура:

Якщо поле StateChange дорівнює DICS_ENABLE, то пристрій буде включено інакше DICS_DISABLE. Якщо поле Scope дорівнює DICS_FLAG_GLOBAL, то зміни набудуть чинності длявсіх апаратних профілів, якщо DICS_FLAG_CONFIGSPECIFIC, зміни набудуть чинності тільки для зазначеного апаратного профілю. Поле HwProfile визначає ID апаратного профілю, до якого будуть застосовуватися зміни, якщо він дорівнює нулю, то поточний апаратний профіль. Усі параметри потребують «затвердження» перед будь-якими змінами. Тому функцію треба викликати двічі. Якщо після першого виклику функція повернула справжнє значення, можна викликати функцію вдруге.

Після зміни стану пристрою необхідно викликати установник класу, т.к. після зміни стану пристрою може знадобитися перезавантаження системи або інші дії, щоб зміни набули чинності. Це здійснюється функцією SetupDiCallClassInstaller

Перший параметр задає код здійсненої операції. Два інші параметри, я думаю, проблем не викличуть.

Як приклад можна навести код увімкнення та відключення мережного підключення. Для того щоб увімкнути/вимкнути мережне підключення достатньо увімкнути/вимкнути мережний пристрій, через який здійснюється мережеве підключення. Це робить така функція:

Параметр index визначає індекс мережного пристрою у списку мережних пристроїв.

Безпечне виймання пристрою

Отже, з увімкненням/вимкненням пристроїв ми розібралися. А як безпечно витягувати пристрій? Безпечне виймання пристрою здійснює функція CM_Request_Device_Eject. Ось її опис:

Перший параметр - це хендл пристрою. Другий параметр - це покажчик на змінну, в яку буде збережено код причини при невдачі. Третій параметр - це вказівник на рядок, в який буде збережено причину невдачі при невдалому виклику. Обидва ці параметри опціональні і можуть дорівнювати нулю. П'ятий параметр цемаксимальна довжина рядка. Шостий не використовується. Якщо pszVetoName дорівнює нулю, то при невдачі повідомлення виведе сама система. Ось і сама функція, яка здійснює безпечне вилучення пристрою.

Єдиний параметр функції, що передається, це індекс дискового пристрою в списку дискових пристроїв. Єдине, що може бути тут незрозуміло - це функція CM_Get_Parent. Вона отримує батько пристрою. Адже будь-яка "флешка" або зовнішній дисковий накопичувач це складовий пристрій і відключати треба саме батьківський пристрій. Код функції IsUSBDevice є у вихідному коді, який додається до статті.

Відстеження змін в апаратній конфігурації

Щоразу, коли відбуваються будь-які зміни в апаратному профілі, головному вікні програми надсилається повідомлення WM_DEVICECHANGE. При отриманні цього повідомлення WParam містить код події. Нас цікавлять лише три коди: DBT_DEVICEARRIVAL, DBT_DEVICEREMOVECOMPLETE та DBT_DEVNODES_CHANGED.

Подія DBT_DEVNODES_CHANGED означає, що зміни в апаратному профілі відбулися. LParam у разі дорівнює нулю. Події DBT_DEVICEARRIVAL та DBT_DEVICEREMOVECOMPLETE ідентичні та відрізняються тим, що перша подія означає приєднання пристрою та друге від'єднання пристрою. LParam відрізняється від нуля за цих подій і вказує на структуру DEV_BROADCAST_HDR. Залежно від поля dbch_devicetype у цій структурі подальші поля можуть змінюватись. Наприклад, якщо dbch_devicetype дорівнює DBT_DEVTYP_VOLUME, то LParam у цьому випадку вказує на структуру DEV_BROADCAST_VOLUME і поле dbcv_unitmask у цій структурі містить бітову маску нових дисків. (нульовий біт позначає букву А, другий букву B, третій букву C тощо).

Наводити код обробника цього повідомлення немає сенсу,він міститься у вихіднику, який додається до статті.

Перший параметр це хендл статусу сервісу чи хендл форми. Параметр NotificationFilter є вказівником на структуру DEV_BROADCAST_HDR і визначає тип пристроїв для відстеження. Для відстеження всіх пристроїв поле dbch_devicetype повинно дорівнювати значенням DBT_DEVTYP_DEVICEINTERFACE. Щоб змінити всі класи пристроїв. Якщо третій параметр дорівнює DEVICE_NOTIFY_WINDOW_HANDLE, перший параметр повинен бути хендлом вікна, якщо DEVICE_NOTIFY_SERVICE_HANDLE, то перший параметр це хендл статусу сервісу. Також для отримання повідомлень про зміну всіх класів пристроїв цей параметр повинен містити прапорець DEVICE_NOTIFY_ALL_INTERFACE_CLASSES.

Ось і кінець цієї статті. До статті додається вихідник на Delphi з прикладом отримання списку пристроїв, прикладом вимкнення пристрою, безпечним вимкненням пристрою, а також веденням лога змін в апаратному профілі.