Zugreifen auf ein USB-Gerät mithilfe von WinUSB-Funktionen

Dieser Artikel enthält eine ausführliche exemplarische Vorgehensweise zur Verwendung von WinUSB-Funktionen für die Kommunikation mit einem USB-Gerät, das Winusb.sys als Funktionstreiber verwendet.

Zusammenfassung

  • Öffnen des Geräts und Abrufen des WinUSB-Handles.
  • Abrufen von Informationen über das Gerät, die Konfiguration und die Schnittstelleneinstellungen aller Schnittstellen und deren Endpunkte.
  • Lesen und Schreiben von Daten für Massen- und Unterbrechungsendpunkte.

Wichtige APIs

Wenn Sie Microsoft Visual Studio 2013 verwenden, erstellen Sie Ihre Skeleton-App mithilfe der WinUSB-Vorlage. Überspringen Sie in diesem Fall die Schritte 1 bis 3, und fahren Sie mit Schritt 4 in diesem Artikel fort. Die Vorlage öffnet ein Dateihandle für das Gerät und ruft das WinUSB-Handle ab, das für nachfolgende Vorgänge erforderlich ist. Dieses Handle wird in der von der App definierten DEVICE_DATA-Struktur in device.h gespeichert.

Weitere Informationen zur Vorlage finden Sie unter Schreiben einer Windows-Desktop-App basierend auf der WinUSB-Vorlage.

Hinweis

WinUSB-Funktionen erfordern Windows XP oder höher. Sie können diese Funktionen in Ihrer C/C++-Anwendung verwenden, um mit Ihrem USB-Gerät zu kommunizieren. Microsoft stellt keine verwaltete API für WinUSB bereit.

Vorbereitung

Die folgenden Punkte gelten für diese exemplarische Vorgehensweise:

  • Diese Informationen gelten für Windows 8.1, Windows 8, Windows 7, Windows Server 2008, Windows Vista-Versionen von Windows.
  • Sie haben Winusb.sys als Funktionstreiber des Geräts installiert. Weitere Informationen zu diesem Prozess finden Sie unter WinUSB -Installation (Winusb.sys).
  • Die Beispiele in diesem Artikel basieren auf dem OSR USB FX2 Learning Kit-Gerät. Sie können diese Beispiele verwenden, um die Verfahren auf andere USB-Geräte zu erweitern.

Schritt 1: Erstellen einer Skeleton-App basierend auf der WinUSB-Vorlage

Um auf ein USB-Gerät zuzugreifen, erstellen Sie zunächst eine Gerüst-App basierend auf der WinUSB-Vorlage, die in der integrierten Umgebung von Windows Driver Kit (WDK) (mit Debugtools für Windows) und Microsoft Visual Studio enthalten ist. Sie können die Vorlage als Ausgangspunkt verwenden.

Informationen zum Vorlagencode, zum Erstellen, Bereitstellen und Debuggen der Skeleton-App finden Sie unter Schreiben einer Windows-Desktop-App basierend auf der WinUSB-Vorlage.

Die Vorlage listet Geräte mithilfe von SetupAPI-Routinen auf, öffnet ein Dateihandle für das Gerät und erstellt ein WinUSB-Schnittstellenhandle, das für nachfolgende Aufgaben erforderlich ist. Beispielcode, der das Gerätehandle abruft und das Gerät öffnet, finden Sie unter Vorlagencodedisk weiter.

Schritt 2: Abfragen des Geräts nach USB-Deskriptoren

Als Nächstes fragen Sie das Gerät nach USB-spezifischen Informationen wie Gerätegeschwindigkeit, Schnittstellendeskriptoren, verwandten Endpunkten und deren Pipes ab. Das Verfahren ähnelt dem verfahren, das USB-Gerätetreiber verwenden. Die Anwendung schließt jedoch Geräteabfragen durch Aufrufen von WinUsb_GetDescriptor ab.

Die folgende Liste zeigt die WinUSB-Funktionen, die Sie aufrufen können, um USB-spezifische Informationen zu erhalten:

  • Weitere Geräteinformationen.

    Rufen Sie WinUsb_QueryDeviceInformation auf, um Informationen von den Gerätedeskriptoren für das Gerät anzufordern. Um die Geschwindigkeit des Geräts abzurufen, legen Sie im InformationType-Parameter DEVICE_SPEED (0x01) fest. Die Funktion gibt LowSpeed (0x01) oder HighSpeed (0x03) zurück.

  • Schnittstellendeskriptoren

    Rufen Sie WinUsb_QueryInterfaceSettings auf, und übergeben Sie die Schnittstellenhandles des Geräts, um die entsprechenden Schnittstellendeskriptoren abzurufen. Das WinUSB-Schnittstellenhandle entspricht der ersten Schnittstelle. Einige USB-Geräte, z. B. das OSR Fx2-Gerät, unterstützen nur eine Schnittstelle ohne alternative Einstellung. Daher wird für diese Geräte der AlternateSettingNumber-Parameter auf 0 festgelegt, und die Funktion wird nur einmal aufgerufen. WinUsb_QueryInterfaceSettings füllt die vom Aufrufer zugeordnete USB_INTERFACE_DESCRIPTOR-Struktur (übergeben im UsbAltInterfaceDescriptor-Parameter ) mit Informationen zur Schnittstelle. Beispielsweise wird die Anzahl der Endpunkte in der Schnittstelle im bNumEndpoints-Element von USB_INTERFACE_DESCRIPTOR festgelegt.

    Rufen Sie für Geräte, die mehrere Schnittstellen unterstützen, WinUsb_GetAssociatedInterface auf, um Schnittstellenhandles für zugeordnete Schnittstellen abzurufen, indem Sie die alternativen Einstellungen im AssociatedInterfaceIndex-Parameter angeben.

  • Endpunkte

    Rufen Sie WinUsb_QueryPipe auf, um Informationen zu jedem Endpunkt auf jeder Schnittstelle zu erhalten. WinUsb_QueryPipe füllt die vom Aufrufer zugeordnete WINUSB_PIPE_INFORMATION-Struktur mit Informationen zur Pipe des angegebenen Endpunkts auf. Die Pipes der Endpunkte werden durch einen nullbasierten Index identifiziert und müssen kleiner als der Wert im bNumEndpoints-Member des Schnittstellendeskriptors sein, der im vorherigen Aufruf von **WinUsb_QueryInterfaceSettings abgerufen wurde. Das OSR Fx2-Gerät verfügt über eine Schnittstelle mit drei Endpunkten. Für dieses Gerät ist der AlternateInterfaceNumber-Parameter der Funktion auf 0 festgelegt, und der Wert des PipeIndex-Parameters variiert von 0 bis 2.

    Um den Pipetyp zu bestimmen, untersuchen Sie den PipeInfo-Member der WINUSB_PIPE_INFORMATION-Struktur. Dieser Member ist auf einen der USBD_PIPE_TYPE Enumerationswerte festgelegt: UsbdPipeTypeControl, UsbdPipeTypeIsochronous, UsbdPipeTypeBulk oder UsbdPipeTypeInterrupt. Das OSR USB FX2-Gerät unterstützt eine Interruptpipe, eine Bulk-In-Pipe und eine Bulk-Out-Pipe, sodass PipeInfo entweder auf UsbdPipeTypeInterrupt oder UsbdPipeTypeBulk festgelegt ist. Der UsbdPipeTypeBulk-Wert identifiziert Massenpipes, gibt aber nicht die Richtung der Pipe an. Die Richtungsinformationen werden im hohen Bit der Pipeadresse codiert, die im PipeId-Element der WINUSB_PIPE_INFORMATION-Struktur gespeichert ist. Die einfachste Möglichkeit, die Richtung der Pipe zu bestimmen, besteht darin, den PipeId-Wert an eines der folgenden Makros von Usb100.h zu übergeben:

    • Das USB_ENDPOINT_DIRECTION_IN (PipeId) Makro gibt TRUE zurück, wenn die Richtung in ist.
    • Das USB_ENDPOINT_DIRECTION_OUT(PipeId) Makro gibt TRUE zurück, wenn die Richtung außerhalb liegt.

    Die Anwendung verwendet den PipeId-Wert , um zu identifizieren, welche Pipe für die Datenübertragung in Aufrufen von WinUSB-Funktionen wie WinUsb_ReadPipe verwendet werden soll (beschrieben im Abschnitt "Issue E/A Requests" dieses Themas), sodass im Beispiel alle drei PipeId-Werte zur späteren Verwendung gespeichert werden.

Der folgende Beispielcode ruft die Geschwindigkeit des Geräts ab, das vom WinUSB-Schnittstellenhandle angegeben wird.

BOOL GetUSBDeviceSpeed(WINUSB_INTERFACE_HANDLE hDeviceHandle, UCHAR* pDeviceSpeed)
{
  if (!pDeviceSpeed || hDeviceHandle==INVALID_HANDLE_VALUE)
  {
    return FALSE;
  }

  BOOL bResult = TRUE;
  ULONG length = sizeof(UCHAR);

  bResult = WinUsb_QueryDeviceInformation(hDeviceHandle, DEVICE_SPEED, &length, pDeviceSpeed);

  if(!bResult)
  {
    printf("Error getting device speed: %d.\n", GetLastError());
    goto done;
  }

  if(*pDeviceSpeed == LowSpeed)
  {
    printf("Device speed: %d (Low speed).\n", *pDeviceSpeed);
    goto done;
  }

  if(*pDeviceSpeed == FullSpeed)
  {
    printf("Device speed: %d (Full speed).\n", *pDeviceSpeed);
    goto done;
  }

  if(*pDeviceSpeed == HighSpeed)
  {
    printf("Device speed: %d (High speed).\n", *pDeviceSpeed);
    goto done;
  }

done:
  return bResult;
}

Im folgenden Beispielcode werden die verschiedenen Deskriptoren für das USB-Gerät abgefragt, das vom WinUSB-Schnittstellenhandle angegeben wird. Die Beispielfunktion ruft die Typen der unterstützten Endpunkte und deren Pipebezeichner ab. Im Beispiel werden alle drei PipeId-Werte zur späteren Verwendung gespeichert.

struct PIPE_ID
{
  UCHAR  PipeInId;
  UCHAR  PipeOutId;
};

BOOL QueryDeviceEndpoints (WINUSB_INTERFACE_HANDLE hDeviceHandle, PIPE_ID* pipeid)
{
  if (hDeviceHandle==INVALID_HANDLE_VALUE)
  {
    return FALSE;
  }

  BOOL bResult = TRUE;

  USB_INTERFACE_DESCRIPTOR InterfaceDescriptor;
  ZeroMemory(&InterfaceDescriptor, sizeof(USB_INTERFACE_DESCRIPTOR));

  WINUSB_PIPE_INFORMATION  Pipe;
  ZeroMemory(&Pipe, sizeof(WINUSB_PIPE_INFORMATION));

  bResult = WinUsb_QueryInterfaceSettings(hDeviceHandle, 0, &InterfaceDescriptor);

  if (bResult)
  {
    for (int index = 0; index < InterfaceDescriptor.bNumEndpoints; index++)
    {
      bResult = WinUsb_QueryPipe(hDeviceHandle, 0, index, &Pipe);

      if (bResult)
      {
        if (Pipe.PipeType == UsbdPipeTypeControl)
        {
          printf("Endpoint index: %d Pipe type: Control Pipe ID: %d.\n", index, Pipe.PipeType, Pipe.PipeId);
        }

        if (Pipe.PipeType == UsbdPipeTypeIsochronous)
        {
          printf("Endpoint index: %d Pipe type: Isochronous Pipe ID: %d.\n", index, Pipe.PipeType, Pipe.PipeId);
        }

        if (Pipe.PipeType == UsbdPipeTypeBulk)
        {
          if (USB_ENDPOINT_DIRECTION_IN(Pipe.PipeId))
          {
            printf("Endpoint index: %d Pipe type: Bulk Pipe ID: %c.\n", index, Pipe.PipeType, Pipe.PipeId);
            pipeid->PipeInId = Pipe.PipeId;
          }

          if (USB_ENDPOINT_DIRECTION_OUT(Pipe.PipeId))
          {
            printf("Endpoint index: %d Pipe type: Bulk Pipe ID: %c.\n", index, Pipe.PipeType, Pipe.PipeId);
            pipeid->PipeOutId = Pipe.PipeId;
          }
        }

        if (Pipe.PipeType == UsbdPipeTypeInterrupt)
        {
          printf("Endpoint index: %d Pipe type: Interrupt Pipe ID: %d.\n", index, Pipe.PipeType, Pipe.PipeId);
        }
      }
      else
      {
        continue;
      }
    }
  }

done:
  return bResult;
}

Schritt 3: Senden der Steuerungsübertragung an den Standardendpunkt

Kommunizieren Sie als Nächstes mit dem Gerät, indem Sie eine Steuerungsanforderung an den Standardendpunkt senden.

Alle USB-Geräte verfügen zusätzlich zu den Endpunkten, die Schnittstellen zugeordnet sind, über einen Standardendpunkt. Der Hauptzweck des Standardendpunkts besteht darin, dem Host Informationen bereitzustellen, die er zum Konfigurieren des Geräts verwenden kann. Geräte können jedoch auch den Standardendpunkt für gerätespezifische Zwecke verwenden. Beispielsweise verwendet das OSR USB FX2-Gerät den Standardendpunkt, um die Lichtleiste und die digitale Anzeige mit sieben Segmenten zu steuern.

Steuerungsbefehle bestehen aus einem 8-Byte-Setuppaket, das einen Anforderungscode enthält, der die jeweilige Anforderung angibt, und einen optionalen Datenpuffer. Die Anforderungscodes und Pufferformate sind vom Anbieter definiert. In diesem Beispiel sendet die Anwendung Daten an das Gerät, um die Lichtleiste zu steuern. Der Code zum Festlegen der Leuchtleiste ist 0xD8, der aus Gründen der Einfachheit als SET_BARGRAPH_DISPLAY definiert wird. Für diese Anforderung benötigt das Gerät einen 1-Byte-Datenpuffer, der angibt, welche Elemente durch Festlegen der entsprechenden Bits beleuchtet werden sollen.

Die Anwendung könnte einen Satz von acht Kontrollkästchen-Steuerelementen bereitstellen, um anzugeben, welche Elemente des Leuchtbalkens beleuchtet werden sollen. Die angegebenen Elemente entsprechen den entsprechenden Bits im Puffer. Um Ui-Code zu vermeiden, legt der Beispielcode in diesem Abschnitt die Bits so fest, dass alternative Lichter leuchtet.

So stellen Sie eine Steuerelementanforderung aus

  1. Ordnen Sie einen 1-Byte-Datenpuffer zu, und laden Sie die Daten in den Puffer, der die Elemente angibt, die durch Festlegen der entsprechenden Bits beleuchtet werden sollen.

  2. Erstellen Sie ein Setuppaket in einer vom Aufrufer zugewiesenen WINUSB_SETUP_PACKET-Struktur . Initialisieren Sie die Member, um den Anforderungstyp und die Daten wie folgt darzustellen:

    • Das RequestType-Element gibt die Anforderungsrichtung an. Er ist auf 0 festgelegt, was die Host-zu-Gerät-Datenübertragung angibt. Legen Sie für Geräte-zu-Host-Übertragungen RequestType auf 1 fest.
    • Das Anforderungsmitglied wird auf den vom Anbieter definierten Code für diese Anforderung festgelegt, 0xD8. Es ist aus Gründen der Einfachheit als SET_BARGRAPH_DISPLAY definiert.
    • Der Length-Member wird auf die Größe des Datenpuffers festgelegt.
    • Die Elemente Index und Value sind für diese Anforderung nicht erforderlich, sodass sie auf 0 (null) festgelegt sind.
  3. Rufen Sie WinUsb_ControlTransfer auf, um die Anforderung an den Standardendpunkt zu übertragen, indem Sie das WinUSB-Schnittstellenhandle des Geräts, das Setuppaket und den Datenpuffer übergeben. Die Funktion empfängt die Anzahl der Bytes, die im LengthTransferred-Parameter auf das Gerät übertragen wurden.

Im folgenden Codebeispiel wird eine Steuerungsanforderung an das angegebene USB-Gerät gesendet, um die Lichter auf der Lichtleiste zu steuern.

BOOL SendDatatoDefaultEndpoint(WINUSB_INTERFACE_HANDLE hDeviceHandle)
{
  if (hDeviceHandle==INVALID_HANDLE_VALUE)
  {
    return FALSE;
  }

  BOOL bResult = TRUE;

  UCHAR bars = 0;

  WINUSB_SETUP_PACKET SetupPacket;
  ZeroMemory(&SetupPacket, sizeof(WINUSB_SETUP_PACKET));
  ULONG cbSent = 0;

  //Set bits to light alternate bars
  for (short i = 0; i < 7; i+= 2)
  {
    bars += 1 << i;
  }

  //Create the setup packet
  SetupPacket.RequestType = 0;
  SetupPacket.Request = 0xD8;
  SetupPacket.Value = 0;
  SetupPacket.Index = 0; 
  SetupPacket.Length = sizeof(UCHAR);

  bResult = WinUsb_ControlTransfer(hDeviceHandle, SetupPacket, &bars, sizeof(UCHAR), &cbSent, 0);

  if(!bResult)
  {
    goto done;
  }

  printf("Data sent: %d \nActual data transferred: %d.\n", sizeof(bars), cbSent);

done:
  return bResult;
}

Schritt 4: Ausgeben von E/A-Anforderungen

Senden Sie als Nächstes Daten an die Bulk-In- und Bulkout-Endpunkte des Geräts, die für Lese- bzw. Schreibanforderungen verwendet werden können. Auf dem OSR USB FX2-Gerät sind diese beiden Endpunkte für Loopback konfiguriert, sodass das Gerät Daten vom Bulk-In-Endpunkt an den Bulkoutendpunkt verschiebt. Der Wert der Daten wird nicht geändert, und es werden keine neuen Daten hinzugefügt. Bei der Loopbackkonfiguration liest eine Leseanforderung die Daten, die von der letzten Schreibanforderung gesendet wurden. WinUSB bietet die folgenden Funktionen zum Senden von Schreib- und Leseanforderungen:

So senden Sie eine Schreibanforderung

  1. Weisen Sie einen Puffer zu, und füllen Sie ihn mit den Daten, die Sie auf das Gerät schreiben möchten. Es gibt keine Einschränkung für die Puffergröße, wenn die Anwendung RAW_IO nicht als Richtlinientyp der Pipe festgelegt. WinUSB unterteilt den Puffer bei Bedarf in entsprechend große Blöcke. Wenn RAW_IO festgelegt ist, wird die Größe des Puffers durch die maximale Übertragungsgröße begrenzt, die von WinUSB unterstützt wird.
  2. Rufen Sie WinUsb_WritePipe auf, um den Puffer auf das Gerät zu schreiben. Übergeben Sie das WinUSB-Schnittstellenhandle für das Gerät, den Pipebezeichner für die Bulkout-Pipe (wie im Abschnitt Abfragen des Geräts nach USB-Deskriptoren dieses Artikels beschrieben) und den Puffer. Die Funktion gibt die Anzahl der Bytes zurück, die im BytesWritten-Parameter auf das Gerät geschrieben werden. Der Überlappungsparameter wird auf NULL festgelegt, um einen synchronen Vorgang anzufordern. Um eine asynchrone Schreibanforderung auszuführen, legen Sie Overlapped auf einen Zeiger auf eine OVERLAPPED-Struktur fest.

Schreibanforderungen, die Daten der Länge Null enthalten, werden über den USB-Stapel weitergeleitet. Wenn die Übertragungslänge größer als eine maximale Übertragungslänge ist, unterteilt WinUSB die Anforderung in kleinere Anforderungen der maximalen Übertragungslänge und übermittelt sie seriell. Im folgenden Codebeispiel wird eine Zeichenfolge zugeordnet und an den Massenoutendpunkt des Geräts gesendet.

BOOL WriteToBulkEndpoint(WINUSB_INTERFACE_HANDLE hDeviceHandle, UCHAR* pID, ULONG* pcbWritten)
{
  if (hDeviceHandle==INVALID_HANDLE_VALUE || !pID || !pcbWritten)
  {
    return FALSE;
  }

  BOOL bResult = TRUE;

  UCHAR szBuffer[] = "Hello World";
  ULONG cbSize = strlen(szBuffer);
  ULONG cbSent = 0;

  bResult = WinUsb_WritePipe(hDeviceHandle, *pID, szBuffer, cbSize, &cbSent, 0);

  if(!bResult)
  {
    goto done;
  }

  printf("Wrote to pipe %d: %s \nActual data transferred: %d.\n", *pID, szBuffer, cbSent);
  *pcbWritten = cbSent;

done:
  return bResult;
}

So senden Sie eine Leseanforderung

  • Rufen Sie WinUsb_ReadPipe auf, um Daten vom Massenendpunkt des Geräts zu lesen. Übergeben Sie das WinUSB-Schnittstellenhandle des Geräts, den Pipebezeichner für den Massenendpunkt und einen leeren Puffer mit entsprechender Größe. Wenn die Funktion zurückgibt, enthält der Puffer die Daten, die vom Gerät gelesen wurden. Die Anzahl der gelesenen Bytes wird im BytesRead-Parameter der Funktion zurückgegeben. Bei Leseanforderungen muss der Puffer ein Vielfaches der maximalen Paketgröße sein.

Leseanforderungen der Länge Null werden sofort erfolgreich abgeschlossen und nicht nach unten im Stapel gesendet. Wenn die Übertragungslänge größer als eine maximale Übertragungslänge ist, unterteilt WinUSB die Anforderung in kleinere Anforderungen der maximalen Übertragungslänge und übermittelt sie seriell. Wenn die Übertragungslänge kein Vielfaches der MaxPacketSize des Endpunkts ist, erhöht WinUSB die Größe der Übertragung auf das nächste Vielfache von MaxPacketSize. Wenn ein Gerät mehr Daten zurückgibt, als angefordert wurden, speichert WinUSB die überschüssigen Daten. Wenn Daten aus einer vorherigen Leseanforderung verbleiben, kopiert WinUSB sie an den Anfang der nächsten Leseanforderung und schließt die Anforderung bei Bedarf ab. Im folgenden Codebeispiel werden Daten aus dem Massenendpunkt des Geräts gelesen.

BOOL ReadFromBulkEndpoint(WINUSB_INTERFACE_HANDLE hDeviceHandle, UCHAR* pID, ULONG cbSize)
{
  if (hDeviceHandle==INVALID_HANDLE_VALUE)
  {
    return FALSE;
  }

  BOOL bResult = TRUE;

  UCHAR* szBuffer = (UCHAR*)LocalAlloc(LPTR, sizeof(UCHAR)*cbSize);
  ULONG cbRead = 0;

  bResult = WinUsb_ReadPipe(hDeviceHandle, *pID, szBuffer, cbSize, &cbRead, 0);

  if(!bResult)
  {
    goto done;
  }

  printf("Read from pipe %d: %s \nActual data read: %d.\n", *pID, szBuffer, cbRead);

done:
  LocalFree(szBuffer);
  return bResult;
}

Schritt 5: Freigeben der Gerätehandles

Nachdem Sie alle erforderlichen Aufrufe des Geräts abgeschlossen haben, geben Sie das Dateihandle und das WinUSB-Schnittstellenhandle für das Gerät frei, indem Sie die folgenden Funktionen aufrufen:

  • CloseHandle , um das handle freizugeben, das von CreateFile erstellt wurde, wie in Schritt 1 beschrieben.
  • WinUsb_Free , um das WinUSB-Schnittstellenhandle für das Gerät freizugeben, das von **WinUsb_Initialize zurückgegeben wird.

Schritt 6: Implementieren der Standard-Funktion

Das folgende Codebeispiel zeigt die Standard-Funktion Ihrer Konsolenanwendung.

Beispielcode, der das Gerätehandle abruft und das Gerät öffnet (in diesem Beispiel GetDeviceHandle und GetWinUSBHandle ), finden Sie unter Diskussion zu Vorlagencode.

int _tmain(int argc, _TCHAR* argv[])
{

  GUID guidDeviceInterface = OSR_DEVICE_INTERFACE; //in the INF file
  BOOL bResult = TRUE;
  PIPE_ID PipeID;
  HANDLE hDeviceHandle = INVALID_HANDLE_VALUE;
  WINUSB_INTERFACE_HANDLE hWinUSBHandle = INVALID_HANDLE_VALUE;
  UCHAR DeviceSpeed;
  ULONG cbSize = 0;

  bResult = GetDeviceHandle(guidDeviceInterface, &hDeviceHandle);

  if(!bResult)
  {
    goto done;
  }

  bResult = GetWinUSBHandle(hDeviceHandle, &hWinUSBHandle);

  if(!bResult)
  {
    goto done;
  }

  bResult = GetUSBDeviceSpeed(hWinUSBHandle, &DeviceSpeed);

  if(!bResult)
  {
    goto done;
  }

  bResult = QueryDeviceEndpoints(hWinUSBHandle, &PipeID);

  if(!bResult)
  {
    goto done;
  }

  bResult = SendDatatoDefaultEndpoint(hWinUSBHandle);

  if(!bResult)
  {
    goto done;
  }

  bResult = WriteToBulkEndpoint(hWinUSBHandle, &PipeID.PipeOutId, &cbSize);

  if(!bResult)
  {
    goto done;
  }

  bResult = ReadFromBulkEndpoint(hWinUSBHandle, &PipeID.PipeInId, cbSize);

  if(!bResult)
  {
    goto done;
  }

  system("PAUSE");

done:
  CloseHandle(hDeviceHandle);
  WinUsb_Free(hWinUSBHandle);

  return 0;
}

Nächste Schritte

Wenn Ihr Gerät isochrone Endpunkte unterstützt, können Sie WinUSB-Funktionen verwenden, um Übertragungen zu senden. Dieses Feature wird nur in Windows 8.1 unterstützt. Weitere Informationen finden Sie unter Senden isochroner USB-Übertragungen von einer WinUSB-Desktop-App.

Weitere Informationen