Windows setup api что это такое

SetupAPI

The Setup application programming interface (SetupAPI) is a system component that provides two sets of functions:

Device installation software can use these functions to perform custom operations in class installers, co-installers, and device installation applications.

For device installation applications, Driver Install Frameworks (DIFx) provides high-level tools that abstract the low-level SetupAPI operations that install Plug and Play (PnP) function drivers and manage the association between application software and the drivers. If the DIFx tools provide the functionality that an installation application requires to install PnP drivers and application software for devices, the installation application should use the DIFx tools instead of directly calling SetupAPI functions. However, co-installers and class installers are Microsoft Win32 DLLs that assist the default installation operation by performing custom operations for a device or all devices in a device setup class. These operations typically require direct calls to Win32 functions and SetupAPI functions.

This section contains the following topics, which provide general information about how to use the general Setup functions and device installation functions that are provided by SetupAPI:

NoteВ В This section describes only a subset of the Setup functions in SetupAPI. For more information about this API, see Setup API .

Статья SetupAPI – информация об устройствах

Перед любым системным программистом, рано или поздно встаёт задача определения текущего оборудования системы. Причин для этого может быть много, например: установить драйвер на девайс, собрать лог всех устройств для последующей работы с ними и т.п. Если мы не будем готовы к таким поворотам судьбы, то придётся переобуваться в воздухе (на скорую руку штудируя доки), в результате чего на выходе получим как-минимум кривой софт.

Да. в штатной поставке Windows имеется оснастка WMI Control – Windows Management Instrumentation (инструментарий управления Win), которая прекрасно решает проблемы данного характера. Однако вызывать сторонние сервисы и приложения из своих программ слишком накладно по времени, и наше приложение будет жутко тормозить. Личные опыты показали, что одну и ту-же задачу WMI решает аж в 60 раз медленнее, чем если этот-же функционал реализовать прямым вызовом системных функций WinAPI – аргумент явно не в пользу WMI, хотя данная оснастка и требует от нас меньших телодвижений.

Так-что оставим этот инструментарий для сис-админов (хотя не факт, что большая часть из них в полной мере знакомы с ним), а мы попробуем собрать инфу об устройствах с подручными средствами, для чего воспользуемся услугами специально предназначенным для этого набором функций под общим названием setupAPI . Этот набор живёт в одноимённой системной библиотеке setupapi.dll, которая выдаёт на экспорт без малого 600-функций. Жалко, что fasm о них ничего не знает и не имеет служебных структур для работы с ней, но это дело поправимое (см.скрепку в конце статьи). Ознакомиться с кол-вом функций и их именами можно в дизассемблере\отладчике W32Dasm, что демонстрирует скрин ниже:

Основное назначение библиотеки setupapi.dll – это установка драйверов устройств и законченная их регистрация в системном реестре. Данная библиотека состоит из двух самостоятельных модулей:

Не зная тонкостей функционирования драйверов (а их в системе вагон и маленькая тележка, и каждый со-своим нравом), конфигуратор трогать рискованно. Поэтому, чтобы-бы после наших экспериментов система не превратилась в труп, в своих программах мы будем использовать исключительно функции SetupDi_XXX. Эта группа функций как-нельзя лучше подходит для тестов и ознакомления, при этом имея довольно мощный функционал.
———————————————

Знакомство с классами устройств, и их идентификаторами GUID

Современная архитектура компьютерных систем имеет огромное количество устройств, и только часть из них внешние. К внешним относятся жёсткий диск, клавиатура, мышь, модем, монитор и прочая ботва, с которой нас знакомили на уроках информатики. Однако внутри чипсета материнской платы зарыты внутренние устройства – это различного рода шины, таймеры, контролёры, мосты и много другое. Более того, в системе могут присутствуют одинаковые по типу, но разные по назначению устройства, а значит их нужно как-то фильтровать.

Читайте также:  Сбросить настройки видеокарты windows 10

Таким образом, все устройства в системе (будь-то внешние или внутренние) разделяются на классы, например: класс шины PCI, класс USB, видео-класс, класс принтеров, модемов, клавиатуры и т.д. В системе, буквально любое устройство обязательно принадлежит к какому-нибудь классу. Каждый класс устройств идентифицируется своим GUID«ом (глобальный уникальный идентификатор), который представляет из-себя 16-байтную (128 битную) запись вида: <50127DC3-0F36-415E-A6CC-4CB3BE910B65>. Схематически это можно представить так:

Информационной базой всех устройств является системный реестр, где для каждого класса-устройств выделена своя ветвь. Все классы собраны в разделе реестра HKLM\SYSTEM\CurrentControlSet\Control\Class. В этой папке мы найдём зарегистрированные когда-либо в системе GUID’ы и даже тех классов, привязанных устройств к которым в текущий момент может и не быть. Например подключили мы к компьютеру месяц назад камеру, а потом убрали её за ненадобность. Это нужно учитывать при сканировании устройств, выставляя флаг DIGCF_PRESENT=2 (только активные девайсы).

При такой\древовидной организации базы-данных, чтобы получить информацию о конкретном устройстве мы должны указать его класс в виде идентификатора GUID. В ответ на этот запрос, система возвратит нам дескриптор данного класса (см.предыдущий рис.), который послужит указателем на соответствующую инфо-базу. Теперь просто сканируем эту базу от подвала до чердака, и получаем из реестра голограмму всех устройств указанного класса.

Забегая вперёд скажу, что для каждого из найденных устройств, функция SetupDiGetDeviceRegistryProperty() как пылесос может вытянуть до 37-ми его характеристик – именно такое количество SPDRP-флагов мы можем передавать ей в аргументе. Как-говорится – хоть лопатой греби..

В талмуде мелкомягких, глобальный идентификатор GUID описывается вполне легальной структурой. Например на рисунке выше, GUID характеризует класс USB-устройств и имеет значение: <36FC9E60-C465-11CF-8056444553540000>. Если копнуть доки, то можно найти значения GUID всех (само)настраивающихся устройств PnP – в представлении ассемблера каждая запись выглядит так, и я собрав их в инклуде setupapi.inc, прикрепил его в скрепке (в штатной поставке fasm’a их нет):

То-есть в значении GUID, первым идёт двойное слово, дальше слово, ещё слово и в конце — массив байт. Поскольку GUID представляет из-себя определяющее класс-устройств уникальное число, то это число эстафетой передаётся от версии к версии Windows. Не знаю как на остальных системах (особенно х64), но на моих эксперементальных XP и Win-7 идентификаторы GUID совпадают, что позволяет писать кросплатформенные приложения на одном инклуде. Кстати Microsoft тоже утверждает, что GUID’ы классов будут соблюдаться ими в последующих версиях операционных систем, иначе зачем нужно было прописывать их в константах сишных хидеров?

Основные API-функции для сбора информации

Будем считать, что с теорией разобрались – перейдём к практической части..
Из указанных 600-функций библиотеки setupapi.dll мы будем использовать всего 6-7. Эти функции позволят нам выжать достаточно информации от первого свидания с этой либой. В качестве демонстрации напишем приложение, которое перечислит все идентификаторы GUID системы и отобразит в читабельном виде, какому именно классу принадлежит тот-или-иной GUID. Здесь в окопах нас поджидают некоторые нюансы – разберём их в кратце..

1. В цикле обходит все классы и возвращает нам их GUID’ы функция CM_Enumerate_Classes() с таким прототипом:

Если коротко, то нам нужно организовать цикл и на каждой его итерации, начиная с нуля увеличивать индекс класса. Эта функция BOOL, так-что если она вернёт EAX=1, значит мы обошли всю базу и пора из цикла выходить. Второй аргумент – это указатель на 16 байтный буфер, в который функция будет сбрасывать GUID текущего индекса. Третий аргумент не используется и должен быть установлен в нуль.

Читайте также:  Itunes 2021 mac os

2. Чтобы привести полученный GUID в читабельную строку (его мы получим в виде hex-значения), задействуем специально предназначенную для этого функцию из библиотеки ole32.dll StringFromGUID2() . Всё-что ей нужно, это указатель на 16-тиричный GUID для преобразования, и указатель на приёмный буфер, куда она сбросит результирующую строку. В случае успеха, fn. возвращает длину записанной в буфер строки. Если получим нуль, значит приёмный буф слишком мал и в аргумент cchMax вернётся трубуемая длинна. Вот её прототип:

Эта функция сбрасывает в буфер GUID в виде Unicode-строки, значит для вывода на консоль её нужно будет преобразовать в Ascii. Для этого будем просто читать по 2-байта, и перезаписывать в тот-же буфер по одному байту (т.е. отсекать парные нули). Вот как выглядит эта информация в секции-данных программы, после того-как функция StringFromGUID2() отработает:

3. На заключительном этапе, чтобы наша GUID-строка несла в себе хоть какую-то информацию, нужно будет по GUID получить строку с именем класса-устройства (см.зелёный блок на рисунке выше). В этом нам поможет функция из setupapi.dll с говорящим за себя названием SetupDiGetClassDescription() . Как и предыдущая, эта функция требует на входе указатель на 16-тиричный GUID, а из своей выхлопной трубы со-свистом выдаёт строковое представление данного класса-устройств (обе функции сами вставляют терминальный нуль в конце):

Из рисунка выше видно, что эта функция возвращает строку в кодировке cp-1251 (зелёный блок), т.е. кириллицей. Если мы планируем выводить информацию в виндовую консоль, то в своём пространстве консоль воспринимает только дос-кодировку OEM-866 . Соответственно если не перекодировать этот выхлоп, то вместо текста, на экране получим инопланетные крякозябры. Для этого воспользуемся функцией из user32.dll под названием CharToOem() , которая изменит кодировку прямо не отходя от кассы, в том-же буфере.

4. Можно прикрутить в исходник код профилировщика, чтобы лицезреть время, потраченное программой на реализацию задуманного. Мы просто на входе в основную процедуру запомним текущие тики системного таймера функцией GetTickCount() , а на выходе из процедуры запросим их опять, и вычислим разницу. Так мы получим хоть какой-то метроном, который отстучит нам потраченное на исполнение кода время. Теме профайлеров мы посвятим отдельную статью, а пока будем довольствоваться этим алго времён динозавров.

Теперь, законченная реализация кода для вывода имени-класса по его GUID может выглядеть примерно так:

Что мы тут имеем? Значит всего классов-устройств в системе равно 60, и соответственно столько-же идентификаторов GUID. Пусть вас не смущает время выполнение данного кода 560 миллисекунд – это тестировалось на виртуальной машине VirtualBox, скорость которой можно сравнить с активностью уставшей улитки. К примеру на скрине ниже я запустил этот-же кодес на реальном процессоре под хр, так хрюша пробежала эту дистанцию всего за 32 мс – как говорится, почувствуйте разницу: 560/32=17.5 раз быстрее:

Перечисляем все, или принадлежащие только к одному классу устройства

Получив GUID’ы классов всех устройств проследуем дальше, и попробуем отфильтровать лог по конкретному классу-устройств, например собрать информацию только об устройствах USB или тех, что повесились на шине PCI и т.п. Такую задачу решает аккорд всего из трёх функций, и все они прописаны в библиотеке setupapi.dll. Обычно эти функции имеют приоритет друг перед другом, т.е. их нужно вызывать в определённой последовательности – рассмотрим её..

1. SetupDiGetClassDevs() – возвращает дескриптор инфо-базы по GUID-класса (см.рис.2), или нуль в случае ошибки. На входе принимает 4 аргумента, первые-три из которых опциональны и могут быть равны нулю (в этом случае фильтр отключается и сканируются все устройства). В природе, на все случаи жизни имеются три распространённых шаблона аргументов этой функции, которые закоментированы в исходнике ниже – это шаблон для всех устройств, фильтр по текстовой маске (возможные варианты: USB/PCI/PCMCIA/SCSI), и фильтрация устройств по классу GUID.

Читайте также:  Delete at job windows

2. SetupDiEnumDeviceInfo() – заполняет структуру SP_DEVINFO_DATA, которая описывает конкретный элемент в классе-устройств. Больше эта функция ничего не делает. Позже, мы должны будем передать указатель на эту структуру третьей функции в этой тусовке, чтобы она черпала с неё информацию. Для перечисления всего списка-устройств указанного класса, нужно поместить данную функцию в цикл, каждый раз увеличивая значение индекса на 1. Эта функция BOOL и возвращает EAX=0 при ошибке (последний элемент в списке):

3. SetupDiGetDeviceRegistryProperty() – последняя, довольно творческая единица и делает всю черновую работу. Именно эта функция лезет в системный реестр, заполняя наш приёмный буфер нарытими данными. Для начала посмотрим на её аргументы, а потом разберёмся с деталями:

Из все этой братии аргументов, нам интересен лишь третий, под кличкой ‘Property’. Он спрашивает у нас, информацию какого характера мы хотим получить? Вот где можно разгуляться с баяном в руках, подставляя в него одну из 37-ми констант (см.инклуд setupapi.inc в скрепке). Эта константа известна как SPDRP – Setup Device Registry Property , или выбор свойства из куста реестра. К сожалению аргумент не позволяет инструкцией OR задавать сразу несколько констант, поэтому если мы хотим за один подход вытянуть несколько строк различной инфы, нужно вызывать эту функцию N-ное количество раз, подставляя в этот аргумент соответствующие значения.

Здесь нужно учитывать, что если нам нужно имя устройства, то оно может хранится в одном из двух полей информационной базы – это Description и Name (зависит от типа устройства). Поэтому чтобы не попасть в просак, для надёжности нужно запрашивать имя сразу два раза – первый раз с аргументом SPDRP_DEVICEDESC , и если функция SetupDiGetDeviceRegistryProperty() вернёт ошибку (или пустую строку в буфере), то второй раз подставить SPDRP_FRIENDLYNAME . Не сбрасывайте это со-счетов..

Без практики, понять эту теоритическую муть довольно сложно, так-что соберём всё сказанное под один колпак и напишем небольшое приложение. Здесь я запрашиваю у системы информацию по GUID’у класса «Контролёры жёстких дисков». В инклуде эта переменная значится как GUID_DEVCLASS_HDC . Дальше, подставив в SPDRP-константу соответствующие значения, получаю: имя, адрес на шине PCI, и вендора обнаруженных устройств. Поиграйтесь с этой константой и получите информацию различного рода:

Здесь мы рассмотрели всего 1% из имеющихся 600 функций в библиотеки setupapi.dll. За бортом осталась довольно могучая SetupDiGetDeviceInterfaceDetail() и многие другие. Однако когда-нибудь нужно сделать первый шаг к покорению этой вершины, что открывает богатые возможности для программирования железа из пользовательского режима. Кстати MSDN хорошо раскрывает эту тему и содержит много полезных материалов – учите и вам обязательно зачтётся.

Под занавес статьи, хочу привести пример bat-файла, который поможет вам искать различные константы в огромном море сишных (и не только) инклуд. Он универсальный и ищет текст по указанной маске, рекурсивно обходя все папки и файлы на жёстком диске. Просто кидаете его в корневую папку и подставляете текст для поиска в аргумент команды FINDSTR между двумя прямыми слэшами. Пошурша некоторое время блинами диска, батник вернём вам директории и имена файлов, где имеется указанный текст – очень удобно (для отображения кириллицы, сохраните его в кодировке OEM-866):

Оцените статью