|
Страница 1 из 4 Функции, которые осуществляют работу с устройствами, находятся в системных библиотеках cfgmgr32.dll и setupapi.dll. К сожалению, в стандартных заголовочных файлах Delphi нет объявлений функций, констант и структур которые используются этими библиотеками. Эти заголовочные файлы можно скачать с сайта проекта Delphi-JEDI. Те, кому не нравятся модули от проекта Delphi-JEDI могут воспользоваться моим модулем setupapi.pas, но в нём далеко не полный список функции и структур.
Получение списка устройствПервая задача, с которой мы столкнёмся это получение списка устройств. Устройства в системе подразделяются на классы, например: класс видеоустройств, принтеров, модемы, клавиатуры и т.д. Любое устройство должно принадлежать как-нибудь классу. Каждый класс идентифицируется своим GUID’ом (глобальный уникальный идентификатор). GUID это 128 битная запись типа: {C06136A2-43EA-4F43-AF06-7413D07E28B7}. Для получения полного списка устройств сначала надо получить список классов. Для получения списка классов используется функция CM_Enumerate_Classes:
CMAPI CONFIGRET WINAPI CM_Enumerate_Classes( IN ULONG ulClassIndex,// индекс класса OUT LPGUID ClassGuid,// указатель GUID класса IN ULONG ulFlags //не используется ); Для перечисления всех классов мы должны в цикле вызывать функцию, начиная с индекса 0. Если функция вернула значение CR_NO_SUCH_VALUE, значит, мы пришли к концу списка. Вторым параметром должен быть указатель на переменную TGUID, в которую будет сохранён GUID класса. Получение информации о классе осуществляет функция SetupDiGetClassDescription:
WINSETUPAPI BOOL WINAPI SetupDiGetClassDescription( IN LPGUID ClassGuid,//GUID класса OUT PTSTR ClassDescription,//строка IN DWORD ClassDescriptionSize,//размер строки OUT PDWORD RequiredSize OPTIONAL//требуемый размер ); Вторым параметром должен идти указатель на буфер, в который будет сохранена строка с именем класса. Третьим параметром должен идти размер передаваемого буфера. Если указанного буфера не хватит, то требуемый размер будет сохранён в переменной указатель, на которую мы передадим четвёртым параметром. Список классов мы получили. Теперь нам надо получить список устройств, принадлежащих некоторому классу. Здесь к нам придёт на помощь функция SetupDiGetClassDevs: HDEVINFO SetupDiGetClassDevs( IN LPGUID ClassGuid, OPTIONAL IN PCTSTR Enumerator, OPTIONAL IN HWND hwndParent, OPTIONAL IN DWORD Flags ); У этой функции почти все параметры опциональны за исключением последнего. Первый параметр задаёт класс устройств для перечисления. Если этот параметр равен нулю, то перечисляться будут все устройства в системе. Второй и третий параметры (соответственно, имя PnP перечислителя и хендл формы) могут быть равны нулю. Последний параметр самый важный. Он может принимать одно из следующий значений или их комбинацию:
- DIGCF_ALLCLASSES
Будет возвращён список всех устройств и всех классов, установленных в данный момент в системе. Первый параметр будет проигнорирован. - DIGCF_DEVICEINTERFACE
Возврат списка устройств, которые поддерживают интерфейсы. - DIGCF_DEFAULT
Возврат списка устройств, которые ассоциируются с системой по умолчанию. - DIGCF_PRESENT
Будет возвращён список устройств, которые в настоящее время присутствуют в системе. - DIGCF_PROFILE
Будет возвращён список устройств, которые являются частью текущего аппаратного профиля.
В нашем случае надо указать только класс устройств и указать последним параметром флаг DIGCF_PRESENT. При успешном вызове функция возвращает хендл полученного списка. Итак, у нас есть список (вернее его хендл) и нам надо как-то перечислить все устройства находящиеся в нём. На помощь к нам придёт функция под названием SetupDiEnumDeviceInfo: WINSETUPAPI BOOL WINAPI SetupDiEnumDeviceInfo( IN HDEVINFO DeviceInfoSet, IN DWORD MemberIndex, OUT PSP_DEVINFO_DATA DeviceInfoData ); С первых параметром, я думаю, всё ясно. Второй парметр задаёт индекс в списке. Третий параметр это указатель на структуру SP_DEVINFO_DATA, в которой будет сохранена информация об устройстве. Если функция вернула значение TRUE, то информация извлечена успешно, а если FALSE, то в большинстве случаев это означает что мы пришли к концу списка. Для перечисления всего списка нам надо будет в цикле вызывать функцию SetupDiEnumDeviceInfo каждый раз увеличивая значение индекса на единицу до тех пор пока не получим отрицательный результат. Итак, у нас есть структура, в которой хранится информация об устройстве:
typedef struct _SP_DEVINFO_DATA { DWORD cbSize; GUID ClassGuid; DWORD DevInst; ULONG_PTR Reserved; } SP_DEVINFO_DATA, *PSP_DEVINFO_DATA;По сути, главным полем здесь является поле DevInst, которая и хранит хендл устройства. Для того чтобы получить имя устройства (или его описание) нам надо использовать функцию SetupDiGetDeviceRegistryProperty. Далее её описание
WINSETUPAPI BOOL WINAPI SetupDiGetDeviceRegistryProperty( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN DWORD Property, OUT PDWORD PropertyRegDataType, OPTIONAL OUT PBYTE PropertyBuffer, IN DWORD PropertyBufferSize, OUT PDWORD RequiredSize OPTIONAL ); Второй параметр это указатель на структуру SP_DEVINFO_DATA. Третий параметр задаёт тип информации, которую мы хотим получить. Для нас важны два флага: SPDRP_FRIENDLYNAME и SPDRP_DEVICEDESC. Далее идёт опциональный параметр который задаёт указатель на переменную в которой будет сохранён тип данных ключа реестра, из которого была извлечена информация. Далее идёт ещё три параметра которые задают соответственно указатель на буфер для сохранения информации, размер буфера и размер реально скопированных данных в ненр. Если мы будем использовать флаг SPDRP_FRIENDLYNAME, то получим вместо модели жёсткого диска «дисковый накопитель», а при использовании флага SPDRP_DEVICEDESC мы получим модель жёсткого диска. Не всегда информация для обоих параметров представлена, иногда есть только для SPDRP_FRIENDLYNAME, а иногда есть только для SPDRP_DEVICEDESC. Если при использовании первого флага мы получили пустую строку, то надо получить информацию с использованием второго флага. Следующая функция получает имя устройства по хендлу перечисления и структуре SP_DEVINFO_DATA. function GetDeviceName(PnPHandle: HDEVINFO; const DevData: TSPDevInfoData): string; var BytesReturned: DWORD; RegDataType: DWORD; Buffer: array [0..256] of CHAR; begin BytesReturned := 0; RegDataType := 0; Buffer[0] := #0; SetupDiGetDeviceRegistryProperty(PnPHandle, DevData, SPDRP_FRIENDLYNAME, RegDataType, PByte(@Buffer[0]), SizeOf(Buffer), BytesReturned); Result := Buffer; if Result<>” then exit; BytesReturned := 0; RegDataType := 0; Buffer[0] := #0; SetupDiGetDeviceRegistryProperty(PnPHandle, DevData, SPDRP_DEVICEDESC, RegDataType, PByte(@Buffer[0]), SizeOf(Buffer), BytesReturned); Result:=Buffer; end; В итоге у нас вырисовывается функция, которая получает список устройств по заданному GUID’у класса. procedure TForm1.AddDevices(aNode: TTreeNode; aGUID: TGUID); var PnPHandle: HDEVINFO; DevData: TSPDevInfoData; RES: LongBool; Devn: Integer; _DN,_PN:ULONG; begin PnPHandle := SetupDiGetClassDevs(@aGUID, nil, 0, DIGCF_PRESENT); if PnPHandle = INVALID_HANDLE_VALUE then Exit; Devn := 0; repeat DevData.cbSize := SizeOf(DevData); RES := SetupDiEnumDeviceInfo(PnPHandle, Devn, DevData); if (RES) and (_DN<>DN_ROOT_ENUMERATED) then begin DeviceTreeView.Items.AddChild(aNode, GetDeviceName(PnPHandle, DevData)); Inc(Devn); end; if Devn=0 then begin DeviceTreeView.Items.Delete(aNode); break; end; until not RES; SetupDiDestroyDeviceInfoList(PnPHandle); end; Данная функция выводит список устройств заданного класса в компонент TreeView. Узел дерева TreeView задаётся первым параметром. Теперь мы можем написать функцию которая и произведёт вывод сего списка устройств в компонент TreeView. Вот она: procedure TForm1.AddAllDevices; var _i:DWORD; Res:CONFIGRET; GUID: PGUID; Buffer: array [0..1023] of CHAR; BufSize: DWORD; Node:TTreeNode; begin DeviceClassesList:=TStringList.Create; _i:=0; repeat GetMem(GUID, SizeOf(TGUID)); Res := CM_Enumerate_Classes(_i, GUID^, 0); if Res <> CR_NO_SUCH_VALUE then begin SetupDiGetClassDescription(GUID^, @Buffer[0], Length(Buffer), BufSize); DeviceClassesList.AddObject(Pchar(@Buffer[0]), TObject(GUID)); end; Inc(_i); until Res = CR_NO_SUCH_VALUE; for _i:=0 to DeviceClassesList.Count-1 do begin Node:=DeviceTreeView.Items.AddChild(nil,DeviceClassesList.Strings[_i]); GUID := PGUID(DeviceClassesList.Objects[_i]); AddDevices(Node,GUID^); end; end; Сначала формируется список строк с имена классов и указателей на их GUID’ы. Потом производится вызов предыдущей функции для каждого класса.
|