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

Интерфейс устройства — это символьная ссылка на устройство Plug and Play (PnP), которое приложение может использовать для доступа к устройству. Приложение в пользовательском режиме может передавать символьное имя интерфейса элементу API, например функции CreateFile Microsoft Win32. Чтобы получить символическое имя ссылки интерфейса устройства, приложение пользовательского режима может вызывать функции диспетчера конфигурации или функции 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 (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 , чтобы получить HANDLE к интерфейсу устройства, он должен зарегистрироваться для уведомления об удалении устройства, чтобы правильно реагировать на попытки удаления запроса. В большинстве случаев драйвер UMDF 2 вызывает CM_Register_Notification только один раз и полагается на поддержку WDF для удаления устройств.

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

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