(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Delphi: Работа с устройствами в Windows

Источник: pblog

http://pblog.ru/?p=105
Функции, которые осуществляют работу с устройствами, находятся в системных библиотеках 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 каждый раз увеличивая значение индекса на единицу до тех пор пока не получим отрицательный результат.
Итак, в Delphi у нас есть структура, в которой хранится информация об устройстве:

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;

В итоге в Delphi  у нас вырисовывается функция, которая получает список устройств по заданному 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"ы. Потом производится вызов предыдущей функции для каждого класса.

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 08.12.2009 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Delphi Professional Named User
Enterprise Connectors (1 Year term)
VMware Fusion 10 Pro, ESD
ARCHICAD 21, локальная лицензия на 12 месяцев
EMS Data Export for PostgreSQL (Business) + 1 Year Maintenance
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Безопасность компьютерных сетей и защита информации
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
СУБД Oracle "с нуля"
Краткие описания программ и ссылки на них
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100