Zugriff auf ein USB-Gerät mithilfe der 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 die Geräte-, Konfigurations- und Schnittstelleneinstellungen aller Schnittstellen und deren Endpunkte.
- Lesen und Schreiben von Daten in Massen- und Unterbrechungsendpunkte.
Wichtige APIs
Wenn Sie Microsoft Visual Studio 2013 verwenden, erstellen Sie Ihre Skelett-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 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.
Vor der Installation
Die folgenden Elemente 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 (Winusb.sys) Installation.
- 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 Skelett-App basierend auf der WinUSB-Vorlage
Um auf ein USB-Gerät zuzugreifen, erstellen Sie zunächst eine Skelett-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, Erstellen, Bereitstellen und Debuggen der Skelett-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 in der Diskussion zu Vorlagencode.
Schritt 2: Abfragen des Geräts nach USB-Deskriptoren
Fragen Sie als Nächstes das Gerät nach USB-spezifischen Informationen wie Gerätegeschwindigkeit, Schnittstellendeskriptoren, verwandten Endpunkten und deren Rohren 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 abzurufen:
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 zu erhalten, legen Sie DEVICE_SPEED (0x01) im Parameter "InformationType " 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 Parameter AlternateSettingNumber auf Null festgelegt, und die Funktion wird nur einmal aufgerufen. WinUsb_QueryInterfaceSettings füllt die vom Aufrufer zugewiesene 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 abzurufen. WinUsb_QueryPipe füllt die vom Aufrufer zugewiesene WINUSB_PIPE_INFORMATION Struktur mit Informationen über die Pipe des angegebenen Endpunkts auf. Die Rohre der Endpunkte werden durch einen nullbasierten Index identifiziert und müssen kleiner als der Wert im bNumEndpoints-Element des Schnittstellendeskriptors sein, der im vorherigen Aufruf von **WinUsb_QueryInterfaceSettings abgerufen wird. Das OSR Fx2-Gerät verfügt über eine Schnittstelle mit drei Endpunkten. Für dieses Gerät wird der AlternateInterfaceNumber-Parameter der Funktion auf 0 festgelegt, und der Wert des PipeIndex-Parameters variiert von 0 bis 2.
Um den Rohrtyp zu ermitteln, untersuchen Sie das PipeInfo-Element der WINUSB_PIPE_INFORMATION Struktur. Dieses Element wird auf einen der USBD_PIPE_TYPE Enumerationswerte festgelegt: UsbdPipeTypeControl, UsbdPipeTypeIsochronous, UsbdPipeTypeBulk oder UsbdPipeTypeInterrupt. Das OSR USB FX2-Gerät unterstützt ein Interruptrohr, ein Bulk-In-Pipe und ein Bulk-Out-Pipe, sodass PipeInfo entweder auf UsbdPipeTypeInterrupt oder UsbdPipeTypeBulk festgelegt ist. Der UsbdPipeTypeBulk-Wert identifiziert Massenrohre, stellt jedoch nicht die Richtung des Rohrs bereit. Die Richtungsinformationen werden im hohen Bit der Pipeadresse codiert, die im PipeId-Element der WINUSB_PIPE_INFORMATION Struktur gespeichert wird. 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 sich die Richtung befindet. - Das
USB_ENDPOINT_DIRECTION_OUT(PipeId)
Makro gibt TRUE zurück, wenn die Richtung ausgeblendet ist.
Die Anwendung verwendet den PipeId-Wert , um zu identifizieren, welche Pipe für die Datenübertragung in Aufrufen von WinUSB-Funktionen verwendet werden soll, z . B. WinUsb_ReadPipe (im Abschnitt "Problem-E/A-Anforderungen" dieses Themas beschrieben), sodass im Beispiel alle drei PipeId-Werte für die spätere Verwendung gespeichert werden.
- Das
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 Pipe-IDs ab. Im Beispiel werden alle drei PipeId-Werte für die spätere 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 Steuerelementübertragung an den Standardendpunkt
Kommunizieren Sie als Nächstes mit dem Gerät, indem Sie die 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 siebensegmente digitale Anzeige zu steuern.
Steuerbefehle 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 Lichtleiste ist 0xD8, der für die Benutzerfreundlichkeit als SET_BARGRAPH_DISPLAY definiert ist. Für diese Anforderung erfordert das Gerät einen 1-Byte-Datenpuffer, der angibt, welche Elemente durch Festlegen der entsprechenden Bits beleuchtet werden sollen.
Die Anwendung könnte eine Reihe von acht Kontrollkästchen-Steuerelementen bereitstellen, um anzugeben, welche Elemente des Lichtbalkens 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 beleuchtet werden.
So stellen Sie eine Steuerelementanforderung aus
Weisen 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.
Erstellen Sie ein Setuppaket in einer vom Anrufer 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. Sie ist auf 0 festgelegt, was die Übertragung von Host-zu-Gerät-Daten angibt. Legen Sie "RequestType" für Geräte-zu-Host-Übertragungen auf 1 fest.
- Das Anforderungsmitglied wird auf den vom Anbieter definierten Code für diese Anforderung 0xD8 festgelegt. Sie ist als "SET_BARGRAPH_DISPLAY" definiert.
- Das Length-Element wird auf die Größe des Datenpuffers festgelegt.
- Die Elemente "Index " und "Value " sind für diese Anforderung nicht erforderlich, sodass sie auf Null festgelegt sind.
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 an das Gerät übertragen wurden.
Im folgenden Codebeispiel wird eine Steuerelementanforderung 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: Problem-E/A-Anforderungen
Senden Sie als Nächstes Daten an die Massen-In- und Massenausgangsendpunkte des Geräts, die für Lese- und Schreibanforderungen verwendet werden können. Auf dem OSR USB FX2-Gerät werden diese beiden Endpunkte für Loopback konfiguriert, sodass das Gerät Daten vom Massenendpunkt zum Massenausgangsendpunkt verschiebt. Der Wert der Daten wird nicht geändert oder neue 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
- Weisen Sie einen Puffer zu, und füllen Sie ihn mit den Daten aus, die Sie auf das Gerät schreiben möchten. Es gibt keine Einschränkung für die Puffergröße, wenn die Anwendung nicht RAW_IO als Richtlinientyp der Pipe festlegt. WinUSB teilt den Puffer bei Bedarf in entsprechend große Blöcke auf. Wenn RAW_IO festgelegt ist, wird die Größe des Puffers durch die maximale Übertragungsgröße begrenzt, die von WinUSB unterstützt wird.
- 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 Massenausgangspipeline (wie im Abschnitt "Abfrage des Geräts für USB-Beschreibungen " dieses Artikels beschrieben) und den Puffer. Die Funktion gibt die Anzahl der Bytes zurück, die in den ByteGeschriebenen Parameter auf das Gerät geschrieben werden. Der Parameter "Overlapped " ist auf NULL festgelegt, um einen synchronen Vorgang anzufordern. Um eine asynchrone Schreibanforderung auszuführen, legen Sie "Overlapped" auf einen Zeiger auf eine ÜBERLAPPENDE Struktur fest.
Schreibanforderungen, die Daten der Länge Null enthalten, werden an den USB-Stapel weitergeleitet. Wenn die Übertragungslänge größer als eine maximale Übertragungslänge ist, teilt WinUSB die Anforderung in kleinere Anforderungen der maximalen Übertragungslänge und sendet sie fortlaufend. Im folgenden Codebeispiel wird eine Zeichenfolge zugewiesen und an den Massenausgangsendpunkt 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 entsprechend großen leeren Puffer. Wenn die Funktion zurückgegeben wird, 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. Für Leseanforderungen muss der Puffer ein Vielfaches der maximalen Paketgröße sein.
Leseanforderungen der Länge Null werden sofort mit Erfolg abgeschlossen und werden nicht an den Stapel gesendet. Wenn die Übertragungslänge größer als eine maximale Übertragungslänge ist, teilt WinUSB die Anforderung in kleinere Anforderungen der maximalen Übertragungslänge und sendet sie fortlaufend. Wenn die Übertragungslänge kein Vielfaches des MaxPacketSize-Objekts 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 wurde, speichert WinUSB die überschüssigen Daten. Wenn Daten aus einer vorherigen Leseanforderung verbleiben, kopiert WinUSB sie bei Bedarf an den Anfang der nächsten Leseanforderung und schließt die Anforderung ab. Im folgenden Codebeispiel werden Daten vom 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 an das Gerät abgeschlossen haben, geben Sie das Dateihandle und das WinUSB-Schnittstellenhandle für das Gerät frei, indem Sie die folgenden Funktionen aufrufen:
- CloseHandle zum Freigeben des Handles, das von CreateFile erstellt wurde, wie in Schritt 1 beschrieben.
- WinUsb_Free das WinUSB-Schnittstellenhandle für das Gerät freizugeben, das von **WinUsb_Initialize zurückgegeben wird.
Schritt 6: Implementieren der Hauptfunktion
Das folgende Codebeispiel zeigt die Hauptfunktion Ihrer Konsolenanwendung.
Beispielcode, der das Gerätehandle abruft und das Gerät öffnet (GetDeviceHandle und GetWinUSBHandle in diesem Beispiel), finden Sie in der 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.