共用方式為


如何取得UWP app (USB描述元)

與 USB 裝置互動的主要工作之一是取得其相關信息。 所有USB裝置都會以稱為描述元的數個數據結構形式提供資訊。 本文說明 UWP app 如何從端點、介面、組態和裝置層級的裝置取得描述項。 本文也涵蓋:

  • 瞭解USB裝置配置
  • 取得標準USB描述元
  • 取得自定義描述項

重要 API

USB 描述元

USB 裝置會在兩個主要描述項中描述其功能:裝置描述元和設定描述元。

USB 裝置必須提供包含USB裝置整體相關信息的 裝置描述項 。 如果裝置未提供該描述元或提供格式不正確的描述元,Windows 就無法載入設備驅動器。 描述元中最重要的資訊是裝置 的硬體標識碼 , (廠商 標識碼與 產品 標識符欄位的組合) 。 它以 Windows 能夠比對裝置內驅動程式的資訊為基礎。 另一個是金鑰的信息是預設端點的最大 封包大小 , (MaxPacketSize0) 。 默認端點是主機傳送至裝置以設定它的所有控制要求目標。

固定裝置描述元的長度。

USB 裝置也必須提供完整的 設定描述元。 此描述元的開頭部分固定長度為 9 個字節,其餘部分取決於這些介面支援的介面數目和端點的可變長度。 固定長度部分提供 USB 組態的相關信息:當裝置位於該組態時,支援和耗電量的介面數目。 這些初始 9 個字節後面接著提供所有 USB 介面相關信息之描述元的變數部分。 每個介面都包含一或多個介面設定,而每個設定是由一組端點所組成。 介面、替代設定和端點的描述項會包含在變數部分。

如需裝置配置的詳細描述,請參閱 標準 USB 描述元

開始之前

  • 您必須已開啟裝置並取得 UsbDevice 物件。 請參閱 如何連線到 USB 裝置 (UWP app)
  • 您可以在 CustomUsbDeviceAccess 範例中看到本主題中顯示的完整程式代碼,Scenario5_UsbDescriptors檔案。
  • 取得裝置配置的相關信息。 適用於 Windows 8) 的 Windows 軟體開發工具組 (SDK) 中包含的Usbview.exe(是一種應用程式,可讓您流覽所有 USB 控制器和與其連線的 USB 裝置。 針對每個連線的裝置,您可以檢視裝置、設定、介面和端點描述元,以了解裝置的功能。

如何取得裝置描述元

您的UWP app可以取得先前取得 UsbDevice 對象的裝置描述元,方法是取得 UsbDevice.DeviceDescriptor 屬性值。

此程式代碼範例示範如何以來自裝置描述元的域值填入字串。

String GetDeviceDescriptorAsString (UsbDevice device)
{
    String content = null;

    var deviceDescriptor = device.DeviceDescriptor;

    content = "Device Descriptor\n"
            + "\nUsb Spec Number : 0x" + deviceDescriptor.BcdUsb.ToString("X4", NumberFormatInfo.InvariantInfo)
            + "\nMax Packet Size (Endpoint 0) : " + deviceDescriptor.MaxPacketSize0.ToString("D", NumberFormatInfo.InvariantInfo)
            + "\nVendor ID : 0x" + deviceDescriptor.IdVendor.ToString("X4", NumberFormatInfo.InvariantInfo)
            + "\nProduct ID : 0x" + deviceDescriptor.IdProduct.ToString("X4", NumberFormatInfo.InvariantInfo)
            + "\nDevice Revision : 0x" + deviceDescriptor.BcdDeviceRevision.ToString("X4", NumberFormatInfo.InvariantInfo)
            + "\nNumber of Configurations : " + deviceDescriptor.NumberOfConfigurations.ToString("D", NumberFormatInfo.InvariantInfo);

    return content;
}

輸出如下所示:

usb 裝置描述元。

如何取得設定描述元

若要從先前取得的 UsbDevice 物件取得組態描述元的固定部分,

  1. UsbDevice 取得 UsbConfiguration 物件。 UsbConfiguration 代表裝置所定義的第一個 USB 組態,而且預設也會由基礎設備驅動器選取。
  2. 取得 UsbConfiguration.ConfigurationDescriptor 屬性值。

組態描述項的固定部分表示裝置的電源特性。 例如,您可以判斷裝置是從總線或外部來源繪製電源, (請參閱 UsbConfigurationDescriptor.SelfPowered) 。 如果裝置從總線繪製電源,) () 以 milliamp 單位為單位的電源 (,請參閱 UsbConfigurationDescriptor.MaxPowerMilliamps) 。 此外,您可以藉由取得 UsbConfigurationDescriptor.RemoteWakeup 值,判斷裝置是否能夠從低電源狀態喚醒或系統。

此程式代碼範例示範如何在字串中取得組態描述元的固定部分。

String GetConfigurationDescriptorAsString(UsbDevice device)
{
    String content = null;

    var usbConfiguration = device.Configuration;
    var configurationDescriptor = usbConfiguration.ConfigurationDescriptor;

    content = "Configuration Descriptor\n"
            + "\nNumber of Interfaces : " + usbConfiguration.UsbInterfaces.Count.ToString("D", NumberFormatInfo.InvariantInfo)
            + "\nConfiguration Value : 0x" + configurationDescriptor.ConfigurationValue.ToString("X2", NumberFormatInfo.InvariantInfo)
            + "\nSelf Powered : " + configurationDescriptor.SelfPowered.ToString()
            + "\nRemote Wakeup : " + configurationDescriptor.RemoteWakeup.ToString()
            + "\nMax Power (milliAmps) : " + configurationDescriptor.MaxPowerMilliamps.ToString("D", NumberFormatInfo.InvariantInfo);

    return content;
}

輸出如下所示:

usb 組態描述元。

如何取得介面描述元

接下來,您可以取得屬於組態一部分的USB介面相關信息。

USB 介面是介面設定的集合。 因此,沒有描述整個介面的描述項。 介面描述項一詞表示描述介面內設定的數據結構。

Windows.Devices.Usb 命名空間會公開物件,讓您可用來取得每個 USB 介面和所有介面描述項的相關信息, (該介面中包含的替代設定) 。 和描述元,來自組態描述元的可變長度部分。

若要從 UsbConfiguration 取得介面描述元,

  1. 取得 UsbConfiguration.UsbInterfaces 屬性,以取得組態內的介面陣列。
  2. 針對每個介面 (UsbInterface) ,請取得此資訊:
    • 大量和中斷使用中且可以傳輸數據的管道。
    • 介面中替代設定的陣列。
    • 介面描述項的陣列。

此範例程式代碼會取得組態的所有 UsbInterface 物件。 從每個物件中,協助程式方法會取得替代設定的數目,並開啟大量和介面管道。 如果裝置支援多個介面,則每個介面的裝置類別、子類別和通訊協議代碼可能會有所不同。 不過,替代設定的所有介面描述項都必須指定相同的程序代碼。 在此範例中,方法會從第一個設定的介面描述元取得裝置類別、子類別和通訊協議程序代碼,以判斷整個介面的程序代碼。

String GetInterfaceDescriptorsAsString(UsbDevice device)
{
    String content = null;

    var interfaces = device.Configuration.UsbInterfaces;

    content = "Interface Descriptors";

        foreach (UsbInterface usbInterface in interfaces)
        {
            // Class/subclass/protocol values from the first interface setting.

            UsbInterfaceDescriptor usbInterfaceDescriptor = usbInterface.InterfaceSettings[0].InterfaceDescriptor;

            content +="\n\nInterface Number: 0x" +usbInterface.InterfaceNumber.ToString("X2", NumberFormatInfo.InvariantInfo)
                    + "\nClass Code: 0x" +usbInterfaceDescriptor.ClassCode.ToString("X2", NumberFormatInfo.InvariantInfo)
                    + "\nSubclass Code: 0x" +usbInterfaceDescriptor.SubclassCode.ToString("X2", NumberFormatInfo.InvariantInfo)
                    + "\nProtocol Code: 0x" +usbInterfaceDescriptor.ProtocolCode.ToString("X2", NumberFormatInfo.InvariantInfo)
                    + "\nNumber of Interface Settings: "+usbInterface.InterfaceSettings.Count.ToString("D", NumberFormatInfo.InvariantInfo)
                    + "\nNumber of open Bulk In pipes: "+usbInterface.BulkInPipes.Count.ToString("D", NumberFormatInfo.InvariantInfo)
                    + "\nNumber of open Bulk Out pipes: "+usbInterface.BulkOutPipes.Count.ToString("D", NumberFormatInfo.InvariantInfo)
                    + "\nNumber of open Interrupt In pipes: "+usbInterface.InterruptInPipes.Count.ToString("D", NumberFormatInfo.InvariantInfo)
                    + "\nNumber of open Interrupt Out pipes: "+usbInterface.InterruptOutPipes.Count.ToString("D", NumberFormatInfo.InvariantInfo);
       }

    return content;
}

輸出如下所示:

usb 介面描述元。

如何取得端點描述元

除了預設控制端點以外,所有USB端點 () 都必須有端點描述元。 若要取得特定端點的端點描述元,您必須知道端點所屬的介面和替代設定。

  1. 取得包含端點的 UsbInterface 物件。

  2. 取得 UsbInterface.InterfaceSettings 以取得替代設定的陣列。

  3. 在陣列中,尋找 (使用端點的 UsbInterfaceSetting) 設定。

  4. 在每個設定中,藉由列舉大量和中斷描述項陣列來尋找端點。

    端點描述項是由這些物件表示:

如果您的裝置只有一個介面,您可以使用 UsbDevice.DefaultInterface 來取得介面,如本範例程式代碼所示。 在這裡,協助程式方法會填入字串,其中包含與使用中介面設定管道相關聯的端點描述元。

private String GetEndpointDescriptorsAsString(UsbDevice device)
{
    String content = null;

    var usbInterface = device.DefaultInterface;
    var bulkInPipes = usbInterface.BulkInPipes;
    var bulkOutPipes = usbInterface.BulkOutPipes;
    var interruptInPipes = usbInterface.InterruptInPipes;
    var interruptOutPipes = usbInterface.InterruptOutPipes;

    content = "Endpoint Descriptors for open pipes";

    // Print Bulk In Endpoint descriptors
    foreach (UsbBulkInPipe bulkInPipe in bulkInPipes)
    {
        var endpointDescriptor = bulkInPipe.EndpointDescriptor;

        content +="\n\nBulk In Endpoint Descriptor"
                + "\nEndpoint Number : 0x" + endpointDescriptor.EndpointNumber.ToString("X2", NumberFormatInfo.InvariantInfo)
                + "\nMax Packet Size : " + endpointDescriptor.MaxPacketSize.ToString("D", NumberFormatInfo.InvariantInfo);
    }

    // Print Bulk Out Endpoint descriptors
    foreach (UsbBulkOutPipe bulkOutPipe in bulkOutPipes)
    {
        var endpointDescriptor = bulkOutPipe.EndpointDescriptor;

        content +="\n\nBulk Out Endpoint Descriptor"
                + "\nEndpoint Number : 0x" + endpointDescriptor.EndpointNumber.ToString("X2", NumberFormatInfo.InvariantInfo)
                + "\nMax Packet Size : " + endpointDescriptor.MaxPacketSize.ToString("D", NumberFormatInfo.InvariantInfo);
    }

    // Print Interrupt In Endpoint descriptors
    foreach (UsbInterruptInPipe interruptInPipe in interruptInPipes)
    {
        var endpointDescriptor = interruptInPipe.EndpointDescriptor;

        content +="\n\nInterrupt In Endpoint Descriptor"
                + "\nEndpoint Number : 0x" + endpointDescriptor.EndpointNumber.ToString("X2", NumberFormatInfo.InvariantInfo)
                + "\nMax Packet Size : " + endpointDescriptor.MaxPacketSize.ToString("D", NumberFormatInfo.InvariantInfo);
                + "\nInterval : " + endpointDescriptor.Interval.Duration.ToString();
    }

    // Print Interrupt Out Endpoint descriptors
    foreach (UsbInterruptOutPipe interruptOutPipe in interruptOutPipes)
    {
        var endpointDescriptor = interruptOutPipe.EndpointDescriptor;

        content +="\n\nInterrupt Out Endpoint Descriptor"
                + "\nEndpoint Number : 0x" + endpointDescriptor.EndpointNumber.ToString("X2", NumberFormatInfo.InvariantInfo)
                + "\nMax Packet Size : " + endpointDescriptor.MaxPacketSize.ToString("D", NumberFormatInfo.InvariantInfo);
                + "\nInterval : " + endpointDescriptor.Interval.Duration.ToString();
    }

    return content;
}

輸出如下所示:

usb 端點描述元。

如何取得自定義描述項

請注意,UsbConfigurationUsbInterface 和 UsbInterfaceSetting 物件,每個物件都會公開名為 Descriptors 的屬性。 該屬性值會擷取 UsbDescriptor 物件所代表的描述項陣列。 UsbDescriptor 物件可讓應用程式取得緩衝區中的描述元數據。 UsbDescriptor.DescriptorTypeUsbDescriptor.Length 屬性會儲存保存描述元所需的緩衝區類型和長度。

注意 所有描述元緩衝區的前兩個字節也會指出描述項的類型和長度。

例如, UsbConfiguration.Descriptors 屬性會取得完整的組態描述元陣列, (固定和可變長度部分) 。 該陣列中的第一個專案是固定長度的組態描述元, (與 UsbConfigurationDescriptor) 相同,第二個專案是第一個替代設定的介面描述元,依此類故。

同樣地, UsbInterface.Descriptors 屬性會取得所有介面描述元和相關端點描述元的陣列。 UsbInterfaceSetting.Descriptors 屬性會取得該設定的所有描述元陣列,例如端點描述元。

當應用程式想要擷取 SuperSpeed 裝置的端點隨附描述元之類的自定義描述元或其他描述項時,取得描述元的方式非常有用。

此程式代碼範例示範如何從組態描述項取得緩衝區中的描述元數據。 此範例會取得組態描述項集,並剖析該集合中包含的所有描述項。 針對每個描述項,它會使用 DataReader 對象來讀取緩衝區,並顯示描述項長度和類型。 您可以取得自定義描述項,如此範例所示。

private String GetCustomDescriptorsAsString(UsbDevice device)
{
    String content = null;
    // Descriptor information will be appended to this string and then printed to UI
    content = "Raw Descriptors";

    var configuration = device.Configuration;
    var allRawDescriptors = configuration.Descriptors;

    // Print first 2 bytes of all descriptors within the configuration descriptor
    // because the first 2 bytes are always length and descriptor type
    // the UsbDescriptor's DescriptorType and Length properties, but we will not use these properties
    // in order to demonstrate ReadDescriptorBuffer() and how to parse it.

    foreach (UsbDescriptor descriptor in allRawDescriptors)
    {
        var descriptorBuffer = new Windows.Storage.Streams.Buffer(descriptor.Length);
        descriptor.ReadDescriptorBuffer(descriptorBuffer);

        DataReader reader = DataReader.FromBuffer(descriptorBuffer);

        // USB data is Little Endian according to the USB spec.
        reader.ByteOrder = ByteOrder.LittleEndian;

        // ReadByte has a side effect where it consumes the current byte, so the next ReadByte will read the next character.
        // Putting multiple ReadByte() on the same line (same variable assignment) may cause the bytes to be read out of order.
        var length = reader.ReadByte().ToString("D", NumberFormatInfo.InvariantInfo);
        var type = "0x" + reader.ReadByte().ToString("X2", NumberFormatInfo.InvariantInfo);

        content += "\n\nDescriptor"
                + "\nLength : " + length
                + "\nDescriptorType : " + type;
    }

    return content;
}