Delphi Работа с устройства в Windows - всичко за ИТ и програмиране
Написано на 26 януари 2009 г. Публикувано в 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. Следва незадължителен параметър, който указва указател към променлива, която ще съхранява типа данни на ключа на системния регистър, от който е извлечена информацията. След това има още три параметъра, които задават съответно указател към буфера за запис на информация, размера на буфера и размера на реално копираните данни в NNR. Ако използваме флага SPDRP_FRIENDLYNAME, ще получим „диск“ вместо модела на твърдия диск, а когато използваме флага SPDRP_DEVICEDESC, ще получим модела на твърдия диск. Не винаги информация заи двете опции са налични, понякога само за SPDRP_FRIENDLYNAME и понякога само за SPDRP_DEVICEDESC. Ако при използване на първия флаг сме получили празен низ, тогава трябва да получим информация, използвайки втория флаг.
Следната функция получава името на устройството от манипулатора enum и структурата 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 указва идентификатора на хардуерния профил, към който ще бъдат приложени промените, ако е нула, тогава текущия хардуерен профил. Всички параметри трябва да бъдат "проверени" преди каквито и да било промени. Следователно функцията трябва да бъде извикана два пъти. Ако след първото извикване функцията е върнала истинска стойност, тогава можете да извикате функцията втори път.
След като промените състоянието на устройството, трябва да се обадите на настройката на класа, защото След промяна на състоянието на устройството може да се наложи рестартиране на системата или други действия, за да влязат в сила промените. Това се прави от функцията 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 в тази структура съдържа битовата маска на новите дискове. (нулевият бит представлява буквата A, втората буква 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 с пример за получаване на списък с устройства, пример за деактивиране на устройство, безопасно деактивиране на устройство и регистриране на промени в хардуерен профил.