USB デバイスへの接続方法 (UWP アプリ)

Windows では、USB デバイスと対話する UWP アプリを作成できます。

この記事では、次の内容について説明します。

  • DeviceWatcher オブジェクトを使用してデバイスを検出する方法
  • コミュニケーション用にデバイスを開く方法
  • 使い終わったときにデバイスを閉じる方法

重要な API

USB デバイスと対話する UWP アプリを作成すると、アプリは制御コマンドを送信し、デバイス情報を取得し、バルク エンドポイントや割り込みエンドポイントとの間でデータの読み取りと書き込みを行うことができます。 すべてを実行する前に、デバイスを見つけて接続を確立する必要があります。

開始する前に

  • これは、シリーズの最初のトピックです。 このチュートリアルを開始する前に、このチュートリアルで拡張できる基本的な Visual Studio プロジェクトを作成しておく必要があります。 詳細については、「UWP アプリの概要」を参照してください。
  • コード例は、CustomUsbDeviceAccess サンプルに基づいています。 完全なサンプルは、このコード ギャラリー ページからダウンロードできます。
  • チュートリアルで使用される USB デバイスは SuperMUTT デバイスです。
  • Windows.Devices.Usb 名前空間を使用して USB デバイスと対話する Windows アプリを作成するには、デバイスにWinusb.sys ドライバーが関数ドライバーとして読み込まれている必要があります。 Winusb.sysは Microsoft によって提供され、Windows の \Windows\System32\drivers フォルダーにあります。

フローチャート: デバイスの検索

USB デバイスに接続するには、まず、さまざまな検出パターンに基づいてデバイスを見つけてから、それに接続する必要があります。

  • 特定のデバイス インターフェイス GUID を使用して、任意の USB デバイスに接続します。
  • 特定のベンダー ID と製品 ID を持ち、特定のデバイス インターフェイス GUID を持つ USB デバイスに接続します。
  • デバイス インターフェイス GUID を知らなくても、特定のベンダー ID と製品 ID を持つ USB デバイスに接続します。
  • 既知のデバイス クラスを持つ USB デバイスに接続します。

usb device discovery.

重要な概念

デバイス インターフェイス GUID とは

カーネル モデル ドライバーは、初期化中に、デバイス インターフェイス GUID と呼ばれる GUID を登録して公開します。 通常、アプリは公開された GUID を使用して、関連付けられているドライバーとそのデバイスを検索し、デバイスへのハンドルを開きます。 取得されたハンドルは、後続の読み取りおよび書き込み操作に使用されます。

ただし、Winusb.sysの場合、ドライバーがデバイス インターフェイス GUID を公開する代わりに、次の 2 つの方法のいずれかで提供できます。

  • デバイスの MS OS 記述子内。 デバイスの製造元は、DeviceInterfaceGUID をデバイスの拡張プロパティ記述子のカスタム プロパティとして設定します。 詳細については、Microsoft OS 記述子の「拡張プロパティ記述子」ドキュメントを参照してください。
  • カスタム INF を使用して Winusb.sys を手動でインストールした場合、INF は GUID を INF に登録します。 「WinUSB (Winusb.sys) のインストール」を参照してください。

デバイスのデバイス インターフェイス GUID が見つかった場合、UWP アプリはそのデバイス インターフェイス GUID に一致するすべてのデバイスを見つけることができます。

Windows で USB デバイスの識別はどのように表示されますか?

すべての USB デバイスには、ベンダー ID と製品 ID という 2 つの情報が必要です。

USB-IF は、これらの識別子を割り当てます。デバイスの製造元は、それらをデバイスで公開する必要があります。 では、その情報を取得するにはどうすればいいでしょうか?

  • デバイスにデバイス ドライバーが読み込まれていない場合でも、Windows はデバイスを「不明なデバイス」として検出します。それでも、ハードウェア ID プロパティ値のデバイス マネージャー内の識別子を表示できます。 この値は、これら 2 つの識別子の組み合わせです。 たとえば、SuperMUTT デバイスの場合、 ハードウェア ID は「USB\VID_045E&PID_F001」、ベンダー ID は「0x045E」で、製品 ID は「0xF001」です。

  • デバイスの INF がある場合は、Models セクションからその文字列を取得します。

  • さまざまなレジストリ設定を調べることができます。 最も簡単な方法は、

    HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Enum\USB\<hardware id>

    詳細については、「USB デバイスのレジストリ エントリ」を参照してください。

  • ハードウェア ID は、デバイスを識別するためにアプリ マニフェストによって使用されます。

    <Device Id="vidpid:045e f001">

UWP アプリは、特定のベンダーと製品 ID に一致するすべてのデバイスを検索できます。 デバイス インターフェイス GUID を指定することで、検索結果を絞り込むことができます。

USB デバイス クラスとは?

ほとんどの USB デバイスは、USB-IF によって承認されたデバイス クラスの仕様に準拠しています。 これらの仕様を使用することで、同様の性質を持つデバイスは、標準の方法で機能を発揮できます。 このアプローチの最大の利点は、デバイスがマイクロソフト提供のインボックス クラス ドライバーまたは汎用 Winusb.sys ドライバーを使用できることです。

一部のデバイスは、USB-IF 仕様に従っていない場合があります。 代わりに、ベンダー定義の機能が公開されます。 このようなデバイスの場合、ベンダーがデバイス ドライバーを提供するか、Winusb.sys を使用する必要があります。

デバイスがベンダー定義であるかデバイス クラスに準拠しているかに関係なく、デバイス クラスに関連する次の情報を記述する必要があります。

  • クラス コード: デバイスが属するデバイス クラスを示します。
  • サブクラス コード: デバイス クラス内で、デバイスのサブクラスを示します。
  • プロトコル コード: デバイスが使用するプロトコル。

たとえば、SuperMUTT デバイスはベンダーが定義したデバイスであり、その情報はクラス コードによって FF として示されます。 デバイスのクラス コードが FEh、サブクラス コードが 02h、プロトコル コードが 00h である場合、そのデバイスはクラス準拠の IrDA ブリッジ デバイスであると結論付けることができます。 UWP アプリは、次のデバイス クラスに属するデバイスと通信できます。

  • ActiveSync
  • CdcControl
  • DeviceFirmwareUpdate
  • IrDA
  • 測定
  • PalmSync
  • PersonalHealthcare
  • 物理
  • VendorSpecific

UWP アプリは、特定のクラス、サブクラス、プロトコル コードのセットに一致するすべてのデバイスを検索できます。

デバイスの高度なクエリ構文 (AQS) 文字列を取得する

検出するデバイスに関する識別情報を含む、高度なクエリ文字列 (AQS) を生成します。 この文字列は、ベンダー/製品 ID、デバイス インターフェイス GUID、またはデバイス クラスを指定して生成できます。

  • ベンダー ID/製品 ID またはデバイス インターフェイス GUID を指定する場合は、GetDeviceSelector のオーバーロードを呼び出します。

    SuperMUTT デバイスの例では、GetDeviceSelector は次のような AQS 文字列を取得します。

    "System.Devices.InterfaceClassGuid:="{DEE824EF-729B-4A0E-9C14-B7117D33A817}" AND System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True AND System.DeviceInterface.WinUsb.UsbVendorId:=1118 AND System.DeviceInterface.WinUsb.UsbProductId:=61441"

    文字列に表示されるデバイス インターフェイス GUID が、指定した GUID ではないことに注意してください。 その GUID は、UWP アプリの Winusb.sys によって登録された実際のデバイス インターフェイス GUID です。

  • デバイスまたはそのクラス、サブクラス、プロトコル コードのデバイス クラスがわかっている場合は、GetDeviceClassSelector を呼び出して AQS 文字列を生成します。

    ClassCodeSubclassCode、および ProtocolCode プロパティ値を指定して、UsbDeviceClass オブジェクトを作成します。 または、デバイスのデバイス クラスがわかっている場合は、特定の UsbDeviceClasses プロパティを指定してコンストラクターを呼び出すことができます。

デバイスの検索 - 基本的な方法

これは、USB デバイスを検索する最も簡単な方法です。 詳細については、「クイック スタート: よく使用されるデバイスの列挙」を参照してください。

  1. 取得した AQS 文字列を FindAllAsync に渡します。 その呼び出しで、DeviceInformationCollection オブジェクトが取得されます。
  2. コレクションをループ処理します。 反復処理によって、DeviceInformation オブジェクトが取得されます。
  3. DeviceInformation.Id プロパティ値を取得します。 文字列値は、デバイス インスタンスのパスです。 たとえば、「\\\\?\\USB#VID_045E&PID_078F#6&1b8ff026&0&5#{dee824ef-729b-4a0e-9c14-b7117d33a817}」などです。
  4. デバイス インスタンス文字列を渡して FromIdAsync を呼び出し、UsbDevice オブジェクトを取得します。 その後、UsbDevice オブジェクトを使用して、制御転送の送信などの他の操作を実行できます。 アプリが UsbDevice オブジェクトの使用を完了したら、Close を呼び出してアプリを解放する必要があります。 : UWP アプリが一時停止すると、デバイスは自動的に閉じられます。 今後の操作で古いハンドルを使用しないようにするには、アプリで UsbDevice 参照を解放する必要があります。
    private async void OpenDevice()
    {
        UInt32 vid = 0x045E;
        UInt32 pid = 0x0611;

        string aqs = UsbDevice.GetDeviceSelector(vid, pid);

        var myDevices = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(aqs);

        try
        {
            usbDevice = await UsbDevice.FromIdAsync(myDevices[0].Id);
        }
        catch (Exception exception)
        {
            ShowStatus(exception.Message.ToString());
        }
        finally
        {
            ShowStatus("Opened device for communication.");
        }

    }

DeviceWatcher を使用してデバイスを検索する

または、デバイスを動的に列挙することもできます。 その後、デバイスが追加または削除された場合、またはデバイスのプロパティが変更された場合に、アプリで通知を受け取ることができます。 詳細については、「デバイスが追加、削除、または変更された場合に通知を受け取る方法」を参照してください。

DeviceWatcher オブジェクトを使用すると、システムからデバイスが追加および削除されると、アプリでデバイスを動的に検出できます。

  1. DeviceWatcher オブジェクトを作成して、デバイスがシステムに追加またはシステムから削除されるタイミングを検出します。 CreateWatcher を呼び出して、AQS 文字列を指定し、オブジェクトを作成する必要があります。

  2. DeviceWatcher オブジェクトに Added および Removed イベントのハンドラーを実装して登録します。 (同じ識別情報を持つ) デバイスがシステムに追加または削除されると、これらのイベント ハンドラーが呼び出されます。

  3. DeviceWatcher オブジェクトを開始および停止します。

    デバイスがシステムに追加またはシステムから削除されると検出を開始できるように、アプリは Start を呼び出して DeviceWatcher オブジェクトを起動する必要があります。 逆に、デバイスを検出する必要がなくなった場合、アプリは Stop を呼び出して DeviceWatcher を停止する必要があります。 このサンプルには、ユーザーが DeviceWatcher を開始および停止できる 2 つのボタンがあります。

このコード例では、SuperMUTT デバイスのインスタンスを検索するデバイス ウォッチャーを作成して起動する方法を示します。

void CreateSuperMuttDeviceWatcher(void)
{
    UInt32 vid = 0x045E;
    UInt32 pid = 0x0611;

    string aqs = UsbDevice.GetDeviceSelector(vid, pid);

    var superMuttWatcher = DeviceInformation.CreateWatcher(aqs);

    superMuttWatcher.Added += new TypedEventHandler<DeviceWatcher, DeviceInformation>
                              (this.OnDeviceAdded);

    superMuttWatcher.Removed += new TypedEventHandler<DeviceWatcher, DeviceInformationUpdate>
                            (this.OnDeviceRemoved);

    superMuttWatcher.Start();
 }

デバイスを開く

デバイスを開くには、アプリで静的メソッド FromIdAsync を呼び出し、デバイス インスタンス パス (DeviceInformation.Id から取得) を渡すことによって非同期操作を開始する必要があります。 その操作が取得された結果は UsbDevice オブジェクトであり、データ転送の実行など、デバイスとの将来のコミュニケーションに使用されます。

UsbDevice オブジェクトの使用が完了したら、それを解放する必要があります。 オブジェクトを解放すると、保留中のすべてのデータ転送が取り消されます。 これらの操作の完了コールバック ルーチンは、取り消されたエラーまたは操作が完了した状態で引き続き呼び出されます。

C++ アプリでは、delete キーワードを使用して参照を解放する必要があります。 C#/VB アプリでは、UsbDevice.Dispose メソッドを呼び出す必要があります。 JavaScript アプリは UsbDevice.Close を呼び出す必要があります。

デバイスが使用中の場合、または見つからない場合、FromIdAsync は失敗します。