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


Использование интерфейсов устройств (WDF)

Интерфейс устройства представляет собой символьную ссылку на устройство Plug and Play (PnP), к которому приложение может получить доступ. Приложение пользовательского режима может передать символьное имя интерфейса элементу API, например функции Microsoft Win32 CreateFile. Чтобы получить символическое имя интерфейса устройства, приложение пользовательского режима может вызывать функции диспетчера конфигурации , или функции SetupApi ,. Дополнительные сведения см. в разделе Перечисление установленных интерфейсов устройств.

Каждый интерфейс устройства принадлежит классу интерфейса устройства. Например, стек драйверов для устройства CD-ROM может предоставить интерфейс, принадлежащий классу GUID_DEVINTERFACE_CDROM. Один из драйверов устройства CD-ROM регистрирует экземпляр класса GUID_DEVINTERFACE_CDROM для информирования системы и приложений о доступности устройства CD-ROM. Дополнительные сведения о классах интерфейса устройства см. в обзор классов интерфейсов устройств.

Регистрация интерфейса устройства

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

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

Для получения сведений о других способах регистрации интерфейсов устройств см. в Регистрация класса интерфейса устройства.

Включение и отключение интерфейса устройства

Интерфейсы, созданные перед запуском устройства (например, из EvtDriverDeviceAdd, EvtChildListCreateDeviceили EvtDevicePrepareHardware) автоматически включены платформой, когда устройство проходит перечисление PnP и запускается. Чтобы предотвратить автоматическое включение интерфейса во время запуска PnP, из той же функции обратного вызова вызовите WdfDeviceSetDeviceInterfaceStateEx и установите параметру EnableInterface у значение FALSE. Сделайте это для данного интерфейса перед началом PnP.

Интерфейсы, созданные после запуска устройства, не будут автоматически включены. Драйвер должен вызывать WdfDeviceSetDeviceInterfaceState или WdfDeviceSetDeviceInterfaceStateEx для включения таких интерфейсов.

Все интерфейсы автоматически отключаются при удалении устройства PnP. Обратите внимание, что изменения состояния питания устройства или перебалансация ресурсов PnP не изменяют состояние интерфейса.

При необходимости драйвер может отключить и повторно включить интерфейс устройства. Например, если драйвер определяет, что устройство перестало отвечать, он может вызвать WdfDeviceSetDeviceInterfaceState или WdfDeviceSetDeviceInterfaceStateEx, чтобы отключить интерфейсы устройства и запретить приложениям получать новые дескрипторы интерфейса. (Существующие дескрипторы интерфейса не затрагиваются.) Если устройство станет доступным позже, драйвер может вызывать WdfDeviceSetDeviceInterfaceState или WdfDeviceSetDeviceInterfaceStateEx повторно для повторного включения интерфейсов.

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

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

Доступ к интерфейсу устройства другого драйвера

В этом разделе показано, как драйвер Kernel-Mode Driver Framework (KMDF) или драйвер User-Mode Driver Framework (UMDF) версии 2 регистрируется для получения уведомлений о прибытии или удалении интерфейса устройства, предоставленного другим драйвером, а затем создает удаленную цель ввода-вывода для обмена данными с устройством, представленным интерфейсом устройства.

Сведения о том, как это сделать в драйвере UMDF версии 1, см. в статье Использование интерфейсов устройств вдрайверов UMDF.

Чтобы зарегистрировать уведомление о событиях интерфейса устройства, драйвер KMDF вызывает IoRegisterPlugPlayNotification, а драйвер UMDF 2 вызывает CM_Register_Notification. В обоих случаях драйвер вызывает соответствующую подпрограмму из функции обратного вызова EvtDriverDeviceAdd .

В следующем примере кода показано, как локальный драйвер UMDF 2 регистрирует уведомления, а затем открывает удаленный целевой объект ввода-вывода.

  1. Удаленный драйвер регистрирует интерфейс устройства путем вызова WdfDeviceCreateDeviceInterface из EvtDriverDeviceAdd.

        UNICODE_STRING ref;
        RtlInitUnicodeString(&ref, MY_HID_FILTER_REFERENCE_STRING);
        status = WdfDeviceCreateDeviceInterface(
                     hDevice,
                     (LPGUID) &GUID_DEVINTERFACE_MY_HIDFILTER_DRIVER,
                     &ref // ReferenceString
                 );
    
        if (!NT_SUCCESS (status)) {
            MyKdPrint( ("WdfDeviceCreateDeviceInterface failed 0x%x\n", status));
            return status;
        }
    
    
  2. Локальный драйвер вызывает CM_Register_Notification из EvtDriverDeviceAdd, чтобы зарегистрировать уведомление при наличии интерфейса устройства. Укажите указатель на подпрограмму обратного вызова уведомлений, которую платформа вызывает, когда доступны интерфейсы устройств.

    DWORD cmRet;
        CM_NOTIFY_FILTER cmFilter;
    
        ZeroMemory(&cmFilter, sizeof(cmFilter));
        cmFilter.cbSize = sizeof(cmFilter);
        cmFilter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE;
        cmFilter.u.DeviceInterface.ClassGuid = GUID_DEVINTERFACE_MY_HIDFILTER_DRIVER;
    
        cmRet = CM_Register_Notification(
                    &cmFilter,                     // PCM_NOTIFY_FILTER pFilter,
                    (PVOID) hDevice,               // PVOID pContext,
                    MyCmInterfaceNotification,    // PCM_NOTIFY_CALLBACK pCallback,
                    &fdoData->CmNotificationHandle // PHCMNOTIFICATION pNotifyContext
                    );
        if (cmRet != CR_SUCCESS) {
            MyKdPrint( ("CM_Register_Notification failed, error %d\n", cmRet));
            status = STATUS_UNSUCCESSFUL;
            return status;
        }   
    
  3. Система вызывает подпрограмму обратного вызова уведомлений локального драйвера при каждом поступлении или удалении указанного интерфейса устройства. Подпрограмма обратного вызова может проверить параметр EventData, чтобы определить, какой интерфейс устройства подключен. Затем он может ставить рабочий элемент в очередь, чтобы открыть интерфейс устройства.

    DWORD 
    MyCmInterfaceNotification(
        _In_ HCMNOTIFICATION       hNotify,
        _In_opt_ PVOID             Context,
        _In_ CM_NOTIFY_ACTION      Action,
        _In_reads_bytes_(EventDataSize) PCM_NOTIFY_EVENT_DATA EventData,
        _In_ DWORD                 EventDataSize
        )
    {
        PFDO_DATA fdoData;
        UNICODE_STRING name;
        WDFDEVICE device;
        NTSTATUS status;
        WDFWORKITEM workitem;
    
        UNREFERENCED_PARAMETER(hNotify);
        UNREFERENCED_PARAMETER(EventDataSize);
    
        device = (WDFDEVICE) Context;
        fdoData = ToasterFdoGetData(device);
    
        switch(Action) {
        case CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL: 
            MyKdPrint( ("MyCmInterfaceNotification: Arrival of %S\n",
                EventData->u.DeviceInterface.SymbolicLink));
    
            //
            // Enqueue a work item to open target
            //
    
            break;
        case CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL: 
            MyKdPrint( ("MyCmInterfaceNotification: removal of %S\n",
                EventData->u.DeviceInterface.SymbolicLink));
            break;
        default:
            MyKdPrint( ("MyCmInterfaceNotification: Arrival unknown action\n"));
            break;
        }
    
        return 0;
    }
    
  4. Из функции обратного вызова рабочего элемента локальный драйвер вызывает WdfIoTargetCreate для создания удаленного целевого объекта и WdfIoTargetOpen, чтобы открыть удаленный целевой объект ввода-вывода.

    При вызове WdfIoTargetOpenдрайвер при необходимости регистрирует функцию обратного вызова EvtIoTargetQueryRemove для получения уведомлений об удалении, а также возможность отказаться от удаления. Если драйвер не предоставляет EvtIoTargetQueryRemove, платформа закрывает целевой объект ввода-вывода при удалении устройства.

    В редких случаях драйвер UMDF 2 может вызывать CM_Register_Notification второй раз, чтобы зарегистрировать уведомление об удалении устройства. Например, если драйвер вызывает CreateFile для получения дескриптора интерфейса устройства, он должен зарегистрироваться для уведомления об удалении устройства, чтобы правильно реагировать на запросы об удалении. В большинстве случаев драйвер UMDF 2 вызывает CM_Register_Notification только один раз и использует поддержку WDF для удаления устройств.

    VOID 
    EvtWorkItem(
        _In_ WDFWORKITEM WorkItem
    )
    {
        // 
        // create and open remote target
        //
    
        return;
    }
    

Регистрация уведомлений о появлении интерфейса устройства и удалении устройства