デバイス インターフェイス (WDF) の使用

デバイス インターフェイスとは、アプリケーションがデバイスへのアクセスに使用できるプラグ アンド プレイ (PnP) デバイスへのシンボリック リンクです。 ユーザー モード アプリケーションは、インターフェイスのシンボリック リンク名を、Microsoft Win32 CreateFile 関数などの API 要素に渡すことができます。 デバイス インターフェイスのシンボリック リンク名を取得するために、ユーザー モード アプリケーションは Configuration Manager 関数または SetupApi 関数を呼び出すことができます。 詳細については、「インストールされているデバイス インターフェイスの列挙」を参照してください。

各デバイス インターフェイスは、デバイス インターフェイス クラスに属しています。 たとえば、CD-ROM デバイスのドライバー スタックは、GUID_DEVINTERFACE_CDROM クラスに属するインターフェイスを提供する場合があります。 CD-ROM デバイスのドライバーの 1 つは、CD-ROM デバイスが使用可能であることをシステムとアプリケーションに通知するために、GUID_DEVINTERFACE_CDROM クラスのインスタンスを登録します。 デバイス インターフェイス クラスの詳細については、「デバイス インターフェイス クラスの概要」を参照してください。

デバイス インターフェイスの登録

デバイス インターフェイス クラスのインスタンスを登録するために、フレームワーク ベースのドライバーは、デバイスの起動前または起動後に WdfDeviceCreateDeviceInterface を呼び出すことができます。 ドライバーがインターフェイスの複数のインスタンスをサポートしている場合は、各インスタンスに一意の参照文字列を割り当てることができます。

ドライバーがデバイス インターフェイスを登録した後、ドライバーは WdfDeviceRetrieveDeviceInterfaceString を呼び出して、システムがデバイス インターフェイスに割り当てたシンボリック リンク名を取得できます。

ドライバーがデバイス インターフェイスを登録するその他の方法については、「デバイス インターフェイス クラスの登録」を参照してください。

デバイス インターフェイスの有効化と無効化

デバイスが起動する前に作成されたインターフェイス (EvtDriverDeviceAddEvtChildListCreateDeviceEvtDevicePrepareHardware など) は、デバイスが PnP 列挙を通過して起動すると、フレームワークによって自動的に有効になります。 PnP の起動中にインターフェイスが自動的に有効にならないようにするには、PnP を開始する前に、そのインターフェイスの同じコールバック関数 (EnableInterface パラメーターを FAL に設定Standard Edition) から WdfDeviceSetDeviceInterfaceStateEx を呼び出します。

デバイスが既に起動した後に作成されたインターフェイスは、自動的には有効になりません。 ドライバーは、このようなインターフェイスを有効にするには、WdfDeviceSetDeviceInterfaceState または WdfDeviceSetDeviceInterfaceStateEx を呼び出す必要があります。

デバイスが PnP の削除を行うと、すべてのインターフェイスが自動的に無効になります。 デバイスの電源状態の変更や PnP リソースの再調整では、インターフェイスの状態は変更されないことに注意してください。

ドライバーは、デバイス インターフェイスを無効にして、必要に応じて再度有効にすることができます。 たとえば、ドライバーがデバイスの応答を停止したと判断した場合、ドライバーは WdfDeviceSetDeviceInterfaceState または WdfDeviceSetDeviceInterfaceStateEx を呼び出してデバイスのインターフェイスを無効にし、アプリケーションがインターフェイスへの新しいハンドルを取得できないようにすることができます。 (インターフェイスに対する既存のハンドルは影響を受けません)。デバイスが後で使用可能になった場合、ドライバーは WdfDeviceSetDeviceInterfaceState または WdfDeviceSetDeviceInterfaceStateEx をもう一度呼び出してインターフェイスを再び有効にすることができます。

デバイス インターフェイスへのアクセス要求の受信

アプリケーションまたはカーネル モード コンポーネントがドライバーのデバイス インターフェイスへのアクセスを要求すると、フレームワークはドライバーの EvtDeviceFileCreate コールバック関数を呼び出します。 ドライバーは、WdfFileObjectGetFileName を呼び出して、アプリケーションまたはカーネル モード コンポーネントがアクセスしているデバイスまたはファイルの名前を取得できます。 ドライバーがデバイス インターフェイスの登録時に参照文字列を指定した場合、オペレーティング システムは WdfFileObjectGetFileName が返すファイル名またはデバイス名に参照文字列を含めます。

別のドライバーのデバイス インターフェイスへのアクセス

このセクションでは、カーネル モード ドライバー フレームワーク (KMDF) ドライバーまたはユーザー モード ドライバー フレームワーク (UMDF) バージョン 2 ドライバーが、別のドライバーによって提供されるデバイス インターフェイスの到着または削除の通知を登録し、デバイス インターフェイスによって表されるデバイスと通信するリモート I/O ターゲットを作成する方法を示します。

UMDF バージョン 1 ドライバーでこれを行う方法については、「UMDF ドライバーでのデバイス インターフェイスの使用」を参照してください。

デバイス インターフェイス イベントの通知に登録するために、KMDF ドライバーは IoRegisterPlugPlayNotification を呼び出し、UMDF 2 ドライバーは CM_Register_Notification を呼び出します。 どちらの場合も、ドライバーは EvtDriverDeviceAdd コールバック関数から適切なルーチンを呼び出します。

次のコード例は、ローカル UMDF 2 ドライバーが通知を登録し、リモート I/O ターゲットを開く方法を示しています。

  1. リモート ドライバーは、EvtDriverDeviceAdd から WdfDeviceCreateDeviceInterface を呼び出すことによって、デバイス インターフェイスに登録します。

        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. ローカル ドライバーは、EvtDriverDeviceAdd から CM_Register_Notification を呼び出して、デバイス インターフェイスが使用可能になったときに通知を登録します。 デバイス インターフェイスが使用可能な場合にフレームワークが呼び出す通知コールバック ルーチンへのポインターを指定します。

    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 を呼び出してリモート I/O ターゲットを開きます。

    WdfIoTargetOpen を呼び出すとき、ドライバーは必要に応じて削除通知を受け取る EvtIoTargetQueryRemove コールバック関数を登録し、削除を拒否する機会を得ることができます。 ドライバーが EvtIoTargetQueryRemove を提供しない場合、フレームワークは、デバイスが削除されたときに I/O ターゲットを閉じます。

    まれに、UMDF 2 ドライバーは 2 回目 CM_Register_Notification 呼び出して、デバイスの削除の通知を登録できます。 たとえば、ドライバーが CreateFile を呼び出してデバイス インターフェイスへの HANDLE を取得する場合は、クエリ削除の試行に適切に応答できるように、デバイスの削除の通知に登録する必要があります。 ほとんどの場合、UMDF 2 ドライバーは 1 回だけ CM_Register_Notification を呼び出し、デバイスの削除に WDF のサポートに依存します。

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

デバイス インターフェイスの到着とデバイスの削除の通知の登録