Поделиться через


Запуск из хранилища драйверов

INF-файл, использующий "run from Driver Store", означает, что INF использует DIRID 13 , чтобы указать расположение для файлов пакетов драйверов при установке.

Для файла, выполняемого из хранилища драйверов, загружаемого INF, подкаталог, указанный в записи SourceDisksFiles для файла в INF, должен соответствовать подкаталогу, указанному в записи DestinationDirs для файла в INF.

Кроме того, директива CopyFiles не может использоваться для переименования файла, выполняемого из Хранилища драйверов. Эти ограничения необходимы, чтобы установка INF на устройстве не приводила к созданию новых файлов в каталоге Хранилища драйверов.

Поскольку записи SourceDisksFiles не могут содержать несколько записей с одинаковыми именами файлов, а CopyFiles нельзя использовать для переименования файла, каждый файл, на который ссылается INF и который выполняется из хранилища драйверов, должен иметь уникальное имя.

Пакеты драйверов имеют общую поддержку запуска из Магазина драйверов, начиная с Windows 10 1709. Однако некоторые стеки устройств могут устанавливать дополнительные ограничения на файлы, необходимые для подключения к этому стеку. Ниже приведены некоторые примеры стека устройств, которые не поддерживают запуск из Магазина драйверов до Windows 10 1803:

  • Двоичные файлы драйверов UMDF: см. раздел «Ограничение расположения загрузки драйверов UMDF» для получения дополнительной информации.

  • Обновление встроенного ПО UEFI. Дополнительные сведения см. в статье "Создание пакета драйвера обновления "

Если вы предоставляете двоичный файл, подключающийся к определенному стеку устройств, обратитесь к документации по конкретному стеку устройств, который вы подключаете к нему, чтобы проверить, поддерживает ли он полный путь к двоичному файлу и если существуют ограничения на полный путь к файлу. Если он поддерживает полный путь к двоичному файлу без ограничений на этот путь, он должен поддерживать файл, запускаемый из Магазина драйверов.

Динамическое поиск и загрузка файлов из хранилища драйверов

Иногда возникает необходимость загрузить файл, который является частью пакета драйвера с функцией "запуск из хранилища драйверов". Пути к этим файлам пакетов драйверов не должны быть жестко закодированы, так как они могут отличаться от разных версий пакета драйвера, разных версий ОС, разных выпусков ОС и т. д. Когда возникает такая необходимость загрузки файлов пакетов драйверов, эти файлы пакетов драйверов должны быть обнаружены и загружены динамически с помощью некоторых парадигм, описанных ниже.

Поиск и загрузка файлов в одном пакете драйверов

Если файл в пакете драйверов должен загрузить другой файл из того же пакета драйверов, один из возможных вариантов динамического обнаружения этого файла — определить каталог, в котором выполняется этот файл, и загрузить другой файл относительно этого каталога.

Драйвер WDM или KMDF, работающий из магазина драйверов в Windows 10 версии 1803 и более поздних версиях, который должен получить доступ к другим файлам из пакета драйвера, должен вызывать IoGetDriverDirectory с DriverDirectoryImage в качестве типа каталога, чтобы получить путь к каталогу, из который был загружен драйвер. В качестве альтернативы, для драйверов, которым требуется поддержка версий ОС до Windows 10 версии 1803, используйте IoQueryFullDriverPath для поиска пути драйвера, получения каталога, из которого он был загружен, и поиска файлов относительно этого пути. Если драйвер режима ядра является драйвером KMDF, он может использовать WdfDriverWdmGetDriverObject для извлечения объекта драйвера WDM для передачи в IoQueryFullDriverPath.

Двоичные файлы в режиме пользователя могут использовать GetModuleHandleExW и GetModuleFileNameW , чтобы определить, откуда был загружен двоичный файл. Например, двоичный файл драйвера UMDF может сделать следующее:

bRet = GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
                         (PCWSTR)&DriverEntry,
                         &handleModule);
if (bRet) {
    charsWritten = GetModuleFileNameW(handleModule,
                                      path,
                                      pathLength);
    …

Поиск и загрузка файлов в любом пакете драйвера

В некоторых сценариях пакет драйвера может содержать файл, который должен быть загружен двоичным файлом в другом пакете драйвера или компонентом пользовательского режима. Этот метод также можно использовать для файлов из того же пакета драйверов, если он предпочтительнее, чем описанный выше метод загрузки файлов из того же пакета драйверов.

Ниже приведены несколько примеров сценариев, которые могут включать загрузку файлов из пакета драйвера:

  • Библиотека DLL в пользовательском режиме в пакете драйвера предоставляет интерфейс для взаимодействия с драйвером в пакете драйвера.

  • Пакет драйвера расширения содержит файл конфигурации, загруженный драйвером в базовом пакете драйвера.

В таких ситуациях пакет драйвера должен задать некоторое состояние на устройстве или интерфейсе устройства, указывающее путь к файлу, который, как ожидается, будет загружен.

Пакет драйвера обычно использует HKR AddReg для задания этого состояния. В этом примере следует предположить, что для ExampleFile.dll пакета драйвера есть запись SourceDisksFiles без подкаталога. Это приводит к тому, что файл находится в корне каталога пакета драйвера. Следует также предположить, что директива DestinationDirs для CopyFiles указывает dirid 13.

Ниже приведен пример INF для задания этого состояния устройства:

[ExampleDDInstall.HW]
AddReg = Example_DDInstall.AddReg

[Example_DDInstall.AddReg]
HKR,,ExampleValue,,%13%\ExampleFile.dll

Пример INF для задания этого состояния интерфейса устройства будет следующим:

[ExampleDDInstall.Interfaces]
AddInterface = {<fill in an interface class GUID for an interface exposed by the device>},,Example_Add_Interface_Section

[Example_Add_Interface_Section]
AddReg = Example_Add_Interface_Section.AddReg

[Example_Add_Interface_Section.AddReg]
HKR,,ExampleValue,,%13%\ExampleFile.dll

В предыдущих примерах используется значение пустых флагов, которое приводит к REG_SZ значению реестра. Это приводит к тому, что %13% превращается в полный путь к файлу в пользовательском режиме. Во многих случаях предпочтительнее определять путь относительно переменной среды. Если используется значение флагов 0x20000 , значение реестра имеет тип REG_EXPAND_SZ, а %13% преобразуется в путь с соответствующими переменными среды, чтобы абстрагировать расположение пути. При получении этого значения реестра вызовите ExpandEnvironmentStrings , чтобы разрешить переменные среды в пути.

Если значение должно считываться компонентом режима ядра, значение должно быть REG_SZ значением. Когда компонент режима ядра считывает это значение, он должен добавлять \??\ перед передачей его в API, например ZwOpenFile.

Чтобы получить доступ к этому параметру, когда он входит в состояние устройства, сначала приложение должно найти идентификацию устройства. Код пользовательского режима может использовать CM_Get_Device_ID_List_Size и CM_Get_Device_ID_List для получения списка устройств, отфильтрованных по мере необходимости. Этот список устройств может содержать несколько устройств, поэтому выполните поиск соответствующего устройства перед чтением состояния с устройства. Например, вызовите CM_Get_DevNode_Property , чтобы получить свойства на устройстве при поиске устройства, соответствующего определенным критериям.

После обнаружения правильного устройства вызовите CM_Open_DevNode_Key, чтобы получить дескриптор на расположение реестра, в котором хранится состояние устройства.

Код режима ядра должен получить PDO (физический объект устройства) у устройства с заданным состоянием и вызвать IoOpenDeviceRegistryKey. Один из возможных способов получения PDO устройства в режиме ядра — обнаружить включенный интерфейс, предоставляемый устройством, и использовать IoGetDeviceObjectPointer для получения объекта устройства.

Чтобы получить доступ к этому параметру при состоянии интерфейса устройства, код пользовательского режима может вызывать CM_Get_Device_Interface_List_Size и CM_Get_Device_Interface_List.

Кроме того, CM_Register_Notification можно использовать для уведомления о поступлении и удалении интерфейсов устройств, чтобы код получал уведомление, когда интерфейс включен, и затем мог получить его состояние. В классе интерфейса устройства может быть несколько интерфейсов устройств, используемых в приведенных выше API. Изучите эти интерфейсы, чтобы определить, какой интерфейс является правильным для чтения параметра.

После обнаружения правильного интерфейса устройства вызовите CM_Open_Device_Interface_Key.

Код режима ядра может получить имя символьной ссылки для интерфейса устройства, из которого требуется получить состояние. Для этого вызовите IoRegisterPlugPlayNotification , чтобы зарегистрировать уведомления интерфейса устройства в соответствующем классе интерфейса устройства. Кроме того, вызовите IoGetDeviceInterfaces , чтобы получить список текущих интерфейсов устройств в системе. В классе интерфейса устройства может быть несколько интерфейсов устройств, используемых в приведенных выше API. Изучите эти интерфейсы, чтобы определить, какой интерфейс является правильным, который должен иметь параметр для чтения.

После обнаружения соответствующего имени символьной ссылки вызовите IoOpenDeviceInterfaceRegistryKey, чтобы получить дескриптор к расположению реестра, где хранится состояние интерфейса устройства.

Примечание.

Используйте флаг CM_GETIDLIST_FILTER_PRESENT с CM_Get_Device_ID_List_Size и CM_Get_Device_ID_List или флаг CM_GET_DEVICE_INTERFACE_LIST_PRESENT с CM_Get_Device_Interface_List_Size и CM_Get_Device_Interface_List. Это гарантирует, что оборудование, связанное с состоянием, содержащим путь к файлу, присутствует и готово к обмену данными.

Удаление пакета драйверов

По умолчанию пакет драйвера не может быть удален из системы, если он по-прежнему установлен на любых устройствах. Однако некоторые возможности удаления пакета драйвера из системы позволяют попытаться его принудительно удалить. Это пытается удалить пакет драйвера, даже если этот пакет драйвера по-прежнему установлен на некоторых устройствах в системе. Принудительное удаление не допускается для пакетов драйверов с файлами, которые выполняются из Хранилища драйверов. Когда пакет драйвера удаляется из системы, его содержимое хранилища драйверов удаляется. Если на каких-либо устройствах все еще установлен этот пакет драйверов, все файлы, выполняемые из хранилища драйверов в этом пакете, теперь будут отсутствовать, и эти отсутствующие файлы могут привести к некорректной работе данного устройства. Чтобы не допустить, чтобы устройство оказалось в нежелательном состоянии, пакеты драйверов, содержащие файлы, выполняемые из Driver Store, не могут быть принудительно удалены. Их можно удалить только после того, как они больше не установлены на каких-либо устройствах. Чтобы помочь в удалении таких пакетов драйверов, можно использовать DiUninstallDriver или pnputil /delete-driver <oem#.inf> /delete . Эти методы удаления сначала обновят все устройства, использующие пакет драйвера, который удаляется, чтобы они больше не были установлены с этим пакетом, прежде чем пытаться удалить сам пакет драйвера.

Разработка пакета драйверов

Тестирование частных двоичных файлов

При разработке пакета драйверов, если необходимо заменить конкретный исполняемый файл из пакета драйвера частной версией вместо полной перестроения и замены пакета драйвера в системе, рекомендуется использовать отладчик ядра вместе с командой Kdfiles . Так как полный путь к файлу в Магазине драйверов не должен быть жестко закодирован, рекомендуется, чтобы в сопоставлении kdfiles имя файла OldDriver было просто прямым именем файла без сведений о предыдущем пути. Чтобы упростить это (и другие сценарии), имена файлов в пакетах драйверов должны быть как можно более уникальными, чтобы он не соответствовал имени файла из не связанного пакета драйвера в системе.

Перенос INF для выполнения из Хранилища драйверов

Если у вас есть существующий пакет драйверов с INF-файлом, который не запускается из Хранилища драйверов, и вы переводите его на запуск из Хранилища драйверов, то в следующих примерах показано общее использование файлов в INF и способы обновления этих файлов для запуска из Хранилища драйверов.

Краткий справочник по обновлениям каталога назначения

В следующей таблице приведен краткий справочник по поиску соответствующих рекомендаций на основе текущего конечного каталога DIRID , указанного в inf-файле пакета драйвера.

DIRID Подкаталог Сведения
13 (тринадцать) Этот файл уже использует режим 'исполнения из Магазина драйверов'. Дальнейшая работа не требуется.
1 DIRID 1 не следует использовать. Нет никакой гарантии, что исходный каталог будет доступен при необходимости разрешения ссылки на файл. Вместо этого, если компоненты пакета драйверов зависят от определенных файлов, включите эти файлы в пакет драйвера и запустите их из хранилища драйверов.
10 Firmware (Встроенное ПО) Сведения о том, как использовать DIRID 13 с пакетом драйвера обновления встроенного ПО для режима «запуск из хранилища драйверов», см. в статье «Создание пакета драйвера обновления».
10 См. другие файлы.
11 См. другие файлы.
12 UMDF См. двоичный файл драйвера UMDF.
12 Большинство файлов с назначением DIRID 12 представляют двоичные файлы службы драйверов. См. двоичный файл службы.
16422, 16426, 16427, 16428 Большинство файлов с назначением этих DIRID представляют установку приложения. Вместо этого предоставьте приложение универсальной платформы Windows (UWP) и установите его с помощью директивы AddSoftware из раздела DDInstall.Software пакета драйвера INF. Дополнительные сведения см. в разделе Связывание драйвера с приложением универсальной платформы Windows (UWP).

Двоичный файл службы

Если ваш INF добавляет службу и двоичный файл не запускается из Хранилища драйверов, то ваш INF может выглядеть следующим образом:

[DestinationDirs]
 ; Copy the file to %windir%\system32\drivers
 Example_CopyFiles = 12

[ExampleDDInstall]
CopyFiles = Example_CopyFiles

[Example_CopyFiles]
ExampleBinary.sys

[ExampleDDInstall.Services]
AddService = ExampleService,0x2,Example_Service_Inst

[Example_Service_Inst]
DisplayName   = %SvcDesc%
ServiceType   = %SERVICE_KERNEL_DRIVER%
StartType     = %SERVICE_DEMAND_START%
ErrorControl  = %SERVICE_ERROR_NORMAL%
; Point at the file in %windir%\system32\drivers
ServiceBinary = %12%\ExampleBinary.sys

Чтобы настроить выполнение этого файла из хранилища драйверов, необходимо обновить запись DestinationDirs, указывающую место, куда будет скопирован файл, и обновить директиву ServiceBinary, ссылающуюся на расположение этого файла.

[DestinationDirs]
; Update the destination to DIRID 13
Example_CopyFiles = 13

[ExampleDDInstall]
CopyFiles = Example_CopyFiles

[Example_CopyFiles]
ExampleBinary.sys

[ExampleDDInstall.Services]
AddService = ExampleService,0x2,Example_Service_Inst

[Example_Service_Inst]
DisplayName   = %SvcDesc%
ServiceType   = %SERVICE_KERNEL_DRIVER%
StartType     = %SERVICE_DEMAND_START%
ErrorControl  = %SERVICE_ERROR_NORMAL%
; Point at the run from Driver Store file using DIRID 13
ServiceBinary = %13%\ExampleBinary.sys

Двоичный файл драйвера UMDF

Если ваш INF добавляет драйвер UMDF, а двоичный файл не запускается из Хранилища драйверов, ваш INF-файл может выглядеть следующим образом:

[DestinationDirs]
; Copy the file to %windir%\system32\drivers\UMDF
Example_CopyFiles = 12, UMDF

[ExampleDDInstall]
CopyFiles = Example_CopyFiles

[Example_CopyFiles]
ExampleUmdfDriver.dll

[ExampleDDInstall.Wdf]
UmdfService = ExampleUmdfDriver,Example_UMDF_Inst
...

[Example_UMDF_Inst]
; Point at the file in %windir%\system32\drivers\UMDF
ServiceBinary = %12%\UMDF\ExampleUmdfDriver.dll
...

Чтобы переместить этот файл для выполнения из хранилища драйверов, необходимо обновить запись DestinationDirs, куда файл будет скопирован, и обновить директиву ServiceBinary, ссылающуюся на расположение этого файла.

[DestinationDirs]
; Update the destination to DIRID 13
Example_CopyFiles = 13

[ExampleDDInstall]
CopyFiles = Example_CopyFiles

[Example_CopyFiles]
ExampleUmdfDriver.dll

[ExampleDDInstall.Wdf]
UmdfService = ExampleUmdfDriver,Example_UMDF_Inst
...

[Example_UMDF_Inst]
; Point at the run from Driver Store file using DIRID 13
ServiceBinary = %13%\ExampleUmdfDriver.dll
...

Другие файлы

Если Ваш INF добавляет файл, который может загружаться другими компонентами и не выполняется из хранилища драйверов, то Ваш INF может выглядеть следующим образом. В этом примере только имя файла записывается в состояние реестра устройства. Компоненты, которые считывают это значение реестра, чтобы определить, какой файл загружать, зависят от того, находится ли файл в %windir%\system32 или могут ли его найти по порядку поиска LoadLibrary.

[DestinationDirs]
; Copy the file to %windir%\system32
Example_CopyFiles = 11

[ExampleDDInstall]
CopyFiles=Example_CopyFiles
AddReg=Example_AddReg

[Example_CopyFiles]
ExampleFile.dll

[Example_AddReg]
HKR,,FileLocation,,"ExampleFile.dll"

Чтобы переместить этот файл для выполнения его из хранилища драйверов, вам необходимо обновить запись DestinationDirs, указав, куда будет скопирован файл, и обновить местоположение, сохранённое в состоянии устройства. Для этого требуются компоненты, которые считывают это значение реестра и способны обрабатывать его как полный путь к файлу, а не как путь к файлу относительно %windir%\system32.

[DestinationDirs]
Example_CopyFiles = 13 ; update the destination to DIRID 13

[ExampleDDInstall]
CopyFiles=Example_CopyFiles
AddReg=Example_AddReg

[Example_CopyFiles]
ExampleFile.dll

[Example_AddReg]
; Point at the run from Driver Store file using DIRID 13
HKR,,FileLocation,,"%13%\ExampleFile.dll"