Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Det enklaste sättet att skriva en Windows-skrivbordsapp som kommunicerar med en USB-enhet är att använda mallen C/C++ WinUSB. För den här mallen behöver du en integrerad miljö med Windows Driver Kit (WDK) (med felsökningsverktyg för Windows) och Microsoft Visual Studio (Professional eller Ultimate). Du kan använda mallen som utgångspunkt.
Innan du börjar
- Om du vill konfigurera den integrerade utvecklingsmiljön installerar du först Microsoft Visual Studio Ultimate 2019 eller Microsoft Visual Studio Professional 2019 och installerar sedan WDK. Du hittar information om hur du konfigurerar Visual Studio och WDK på WDK-nedladdningssidan.
- Felsökningsverktyg för Windows ingår när du installerar WDK. Mer information finns i Ladda ned och installera felsökningsverktyg för Windows.
Skapa ett WinUSB-program
Så här skapar du ett program från mallen:
I dialogrutan Nytt projekt skriver du USB i sökrutan längst upp.
I den mellersta rutan väljer du WinUSB-program (universal).
Välj Nästa.
Ange ett projektnamn, välj en plats för att spara och välj Skapa.
Följande skärmbilder visar dialogrutan Nytt projekt för WinUSB-applikationsmall (Universal).
Det här avsnittet förutsätter att namnet på Visual Studio-projektet är USB Application1.
Visual Studio skapar ett projekt och en lösning. Du kan se lösningen, projektet och filerna som tillhör projektet i Solution Explorer-fönstret , som du ser i följande skärmbild. (Om Solution Explorer-fönstret inte visas väljer du Solution Explorer på menyn Visa .) Lösningen innehåller ett C++-programprojekt med namnet USB Application1.
USB Application1-projektet har källfiler för programmet. Om du vill titta på programmets källkod kan du öppna alla filer som visas under Källfiler.
Lägg till ett drivrutinspaketprojekt i lösningen. Välj och håll (eller högerklicka) lösningen (lösning "USB Application1") och välj sedan Lägg till>nytt projekt enligt följande skärmbild.
I dialogrutan Nytt projekt skriver du USB igen i sökrutan längst upp .
I den mellersta rutan väljer du WinUSB INF-drivrutinspaket.
Välj Nästa.
Ange ett projektnamn och välj sedan Skapa.
Följande skärmbilder visar dialogrutan Nytt projekt för mallen WinUSB INF-drivrutinspaket .
Det här avsnittet förutsätter att namnet på Visual Studio-projektet är USB Application1 Package.
Projektet USB Application1 Package innehåller en INF-fil som används för att installera Microsoft-tillhandahållen Winusb.sys drivrutin som enhetsdrivrutin.
Solution Explorer bör nu innehålla båda projekten, enligt följande skärmbild.
Leta upp den här koden i INF-filen USBApplication1.inf:
%DeviceName% =USB_Install, USB\VID_vvvv&PID_pppp
Ersätt VID_vvvv&PID_pppp med enhetens maskinvaru-ID. Hämta maskinvaru-ID:t från Enhetshanteraren. I Enhetshanteraren visar du enhetsegenskaperna. På fliken Detaljer visa egenskapsvärdet Maskinvaru-ID.
I fönstret Solution Explorer väljer du och håller (eller högerklickar på) lösningen "USB Application1" (2 av 2 projekt) och väljer Configuration Manager. Välj en konfiguration och plattform för både programprojektet och paketprojektet. I den här övningen väljer vi Felsökning och x64, enligt följande skärmbild.
Skapa, distribuera och felsöka projektet
Hittills i den här övningen har du använt Visual Studio för att skapa dina projekt. Därefter måste du konfigurera den enhet som enheten är ansluten till. Mallen kräver att Winusb-drivrutinen installeras som drivrutin för din enhet.
Din test- och felsökningsmiljö kan ha:
Två datorinstallationer: värddatorn och måldatorn. Du utvecklar och skapar ditt projekt i Visual Studio på värddatorn. Felsökningsprogrammet körs på värddatorn och är tillgängligt i Visual Studio-användargränssnittet. När du testar och felsöker programmet körs drivrutinen på måldatorn.
En datoruppsättning: Din mål- och värddator körs på samma dator. Du utvecklar och skapar projektet i Visual Studio och kör felsökningsprogrammet och programmet.
Du kan distribuera, installera, läsa in och felsöka ditt program och drivrutinen genom att följa dessa steg:
Två datorinstallationer
- Etablera måldatorn genom att följa anvisningarna i Etablera en dator för distribution och testning av drivrutiner. Obs: Provisionering skapar en användare på måldatorn som heter WDKRemoteUser. När etableringen är klar växlar användaren till WDKRemoteUser.
- Öppna lösningen i Visual Studio på värddatorn.
- Lägg till den här raden före OpenDevice-anropet i main.cpp.
system ("pause")
Raden gör att programmet pausas när det startas. Detta är användbart vid fjärrfelsökning.
- I pch.h inkluderar du den här raden:
#include <cstdlib>
Den här inkluderingsinstruktionen krävs för
system()
-anropet i föregående steg.I fönstret Solution Explorer väljer du och håller (eller högerklickar på) USB Application1 Package och väljer Egenskaper.
I fönstret USB Application1 Package Property Pages, i den vänstra panelen, navigerar du till Egenskaper för konfiguration> Drivrutinsinstallation> Distribution, som visas i följande skärmbild.
Kontrollera Ta bort tidigare drivrutinsversioner innan distributionen.
För Fjärrdatornamnväljer du namnet på den dator som du konfigurerade för testning och felsökning. I den här övningen använder vi en dator med namnet dbg-target.
Välj Installera/installera om och verifiera. Välj Använd.
På egenskapssidan går du till Felsökning av konfigurationsegenskaper >och väljer Felsökningsverktyg för Windows – Fjärrfelsökare, som du ser i följande skärmbild.
Välj Skapa lösning på menyn Skapa . Visual Studio visar byggframsteg i utdatafönstret . (Om utdatafönstret inte visas väljer du Utdata på menyn Visa .) I den här övningen har vi skapat projektet för ett x64-system som kör Windows 10.
Välj Distribuera lösning på menyn Skapa .
På måldatorn visas drivrutinsinstallationsskript som körs. Drivrutinsfilerna kopieras till mappen %Systemdrive%\drivertest\drivers på måldatorn. Kontrollera att filerna .inf, .cat, test cert och .sys och andra nödvändiga filer finns %systemdrive%mappen \drivertest\drivers. Enheten måste visas i Enhetshanteraren utan fel.
På värddatorn visas det här meddelandet i fönstret Utdata .
Deploying driver files for project
"<path>\visual studio 14\Projects\USB Application1\USB Application1 Package\USB Application1 Package.vcxproj".
Deployment may take a few minutes...
========== Build: 1 succeeded, 0 failed, 1 up-to-date, 0 skipped ==========
Så här felsöker du programmet
På värddatorn navigerar du till x64 > Win8.1Debug i lösningsmappen.
Kopiera det körbara programmet UsbApplication1.exe till måldatorn.
Starta programmet på måldatorn.
På värddatorn går du till felsökningsmenyn och väljer Anslut till process.
I fönstret väljer du Felsökningsprogram för Windows-användarläge (Felsökningsverktyg för Windows) som transport och namnet på måldatorn, i det här fallet dbg-target, som kvalificerare som visas i den här bilden.
Välj programmet i listan över tillgängliga processer och välj Bifoga. Du kan nu felsöka med omedelbart fönster eller med hjälp av alternativen i felsökningsmenyn.
Föregående instruktioner felsöker programmet med hjälp av felsökningsverktyg för Windows – Fjärrfelsökare. Om du vill använda felsökaren för Fjärr-Windows (felsökningsprogrammet som ingår i Visual Studio) följer du dessa instruktioner:
- På måldatorn lägger du till msvsmon.exe i listan över appar som tillåts via brandväggen.
- Starta Visual Studio Remote Debugging Monitor som finns i C:\DriverTest\msvsmon\msvsmon.exe.
- Skapa en arbetsmapp, till exempel C:\remotetemp.
- Kopiera det körbara programmet UsbApplication1.exe till arbetsmappen på måldatorn.
- Högerklicka på projektet USB Application1 Package på värddatorn i Visual Studio och välj Ta bort projekt.
- Välj och håll (eller högerklicka på) USB Application1-projektet . I projektegenskaper expanderar du noden Konfigurationsegenskaper och väljer Felsökning.
- Ändra felsökningsprogrammet så att det startas till Fjärrfelsökaren för Windows.
- Ändra projektinställningarna för att köra den körbara filen på en fjärrdator genom att följa anvisningarna i Fjärrfelsökning av ett projekt som skapats lokalt. Kontrollera att egenskaperna Arbetskatalog och Fjärrkommando återspeglar mappen på måldatorn.
- Om du vill felsöka programmet går du till menyn Skapa och väljer Starta felsökning eller trycker på F5.
Installation av en enda dator:
Om du vill skapa programmet och drivrutinsinstallationspaketet väljer du Skapa lösning på menyn Skapa . Visual Studio visar byggframsteg i utdatafönstret . (Om utdatafönstret inte visas väljer du Utdata på menyn Visa .) I den här övningen har vi skapat projektet för ett x64-system som kör Windows 10.
Om du vill se det byggda drivrutinspaketet navigerar du i Utforskaren till din USB Application1-mapp och går sedan till x64 > Felsöka > USB Application1-paket. Drivrutinspaketet innehåller flera filer: MyDriver.inf är en informationsfil som Windows använder när du installerar drivrutinen, mydriver.cat är en katalogfil som installationsprogrammet använder för att verifiera testsignaturen för drivrutinspaketet. Dessa filer visas i följande skärmbild.
Det finns ingen drivrutinsfil som ingår i paketet. Det beror på att INF-filen refererar till in-box-drivrutinen, Winusb.sys, som finns i mappen Windows\System32.
Installera drivrutinen manuellt. I Enhetshanteraren uppdaterar du drivrutinen genom att specificera INF-filen i paketet. Peka på drivrutinspaketet som finns i lösningsmappen, som visas i föregående avsnitt. Om du ser felet
DriverVer set to a date in the future
anger du INF-paketprojektinställningar > Inf2Cat > Allmänt > Använd lokal tid > Ja.Välj och håll (eller högerklicka på) USB Application1-projektet . I projektegenskaper expanderar du noden Konfigurationsegenskaper och väljer Felsökning.
Ändra felsökningsprogrammet så att det startas till Lokalt Windows-felsökningsprogram.
Välj och håll (eller högerklicka på) USB Application1-paketprojektet och välj Ta bort projekt.
Om du vill felsöka programmet går du till menyn Skapa och väljer Starta felsökning eller trycker på F5.
Mallkodsdiskussion
Mallen är en startpunkt för ditt skrivbordsprogram. USB Application1-projektet har källfiler device.cpp och main.cpp.
Filen main.cpp innehåller programmets startpunkt _tmain. Device.cpp innehåller alla hjälpfunktioner som öppnar och stänger handtaget till enheten.
Mallen har också en huvudfil med namnet device.h. Den här filen innehåller definitioner för enhetsgränssnittets GUID (beskrivs senare) och en DEVICE_DATA struktur som lagrar information som hämtas av programmet. Till exempel lagrar den WinUSB-gränssnittshandtaget som hämtas av OpenDevice och används i efterföljande åtgärder.
typedef struct _DEVICE_DATA {
BOOL HandlesOpen;
WINUSB_INTERFACE_HANDLE WinusbHandle;
HANDLE DeviceHandle;
TCHAR DevicePath[MAX_PATH];
} DEVICE_DATA, *PDEVICE_DATA;
Hämta instanssökvägen för enheten – se RetrieveDevicePath i device.cpp
För att få åtkomst till en USB-enhet skapar programmet ett giltigt filhandtag för enheten genom att anropa CreateFile. För det anropet måste programmet hämta enhetens sökvägsinstans. För att hämta enhetssökvägen använder appen SetupAPI-rutiner och anger enhetsgränssnittets GUID i INF-filen som användes för att installera Winusb.sys. Device.h deklarerar en GUID-konstant med namnet GUID_DEVINTERFACE_USBApplication1. Genom att använda dessa rutiner räknar programmet upp alla enheter i den angivna enhetsgränssnittsklassen och hämtar enhetens enhetssökväg.
HRESULT
RetrieveDevicePath(
_Out_bytecap_(BufLen) LPTSTR DevicePath,
_In_ ULONG BufLen,
_Out_opt_ PBOOL FailureDeviceNotFound
)
/*++
Routine description:
Retrieve the device path that can be used to open the WinUSB-based device.
If multiple devices have the same device interface GUID, there is no
guarantee of which one will be returned.
Arguments:
DevicePath - On successful return, the path of the device (use with CreateFile).
BufLen - The size of DevicePath's buffer, in bytes
FailureDeviceNotFound - TRUE when failure is returned due to no devices
found with the correct device interface (device not connected, driver
not installed, or device is disabled in Device Manager); FALSE
otherwise.
Return value:
HRESULT
--*/
{
BOOL bResult = FALSE;
HDEVINFO deviceInfo;
SP_DEVICE_INTERFACE_DATA interfaceData;
PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = NULL;
ULONG length;
ULONG requiredLength=0;
HRESULT hr;
if (NULL != FailureDeviceNotFound) {
*FailureDeviceNotFound = FALSE;
}
//
// Enumerate all devices exposing the interface
//
deviceInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USBApplication1,
NULL,
NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (deviceInfo == INVALID_HANDLE_VALUE) {
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
//
// Get the first interface (index 0) in the result set
//
bResult = SetupDiEnumDeviceInterfaces(deviceInfo,
NULL,
&GUID_DEVINTERFACE_USBApplication1,
0,
&interfaceData);
if (FALSE == bResult) {
//
// We would see this error if no devices were found
//
if (ERROR_NO_MORE_ITEMS == GetLastError() &&
NULL != FailureDeviceNotFound) {
*FailureDeviceNotFound = TRUE;
}
hr = HRESULT_FROM_WIN32(GetLastError());
SetupDiDestroyDeviceInfoList(deviceInfo);
return hr;
}
//
// Get the size of the path string
// We expect to get a failure with insufficient buffer
//
bResult = SetupDiGetDeviceInterfaceDetail(deviceInfo,
&interfaceData,
NULL,
0,
&requiredLength,
NULL);
if (FALSE == bResult && ERROR_INSUFFICIENT_BUFFER != GetLastError()) {
hr = HRESULT_FROM_WIN32(GetLastError());
SetupDiDestroyDeviceInfoList(deviceInfo);
return hr;
}
//
// Allocate temporary space for SetupDi structure
//
detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)
LocalAlloc(LMEM_FIXED, requiredLength);
if (NULL == detailData)
{
hr = E_OUTOFMEMORY;
SetupDiDestroyDeviceInfoList(deviceInfo);
return hr;
}
detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
length = requiredLength;
//
// Get the interface's path string
//
bResult = SetupDiGetDeviceInterfaceDetail(deviceInfo,
&interfaceData,
detailData,
length,
&requiredLength,
NULL);
if(FALSE == bResult)
{
hr = HRESULT_FROM_WIN32(GetLastError());
LocalFree(detailData);
SetupDiDestroyDeviceInfoList(deviceInfo);
return hr;
}
//
// Give path to the caller. SetupDiGetDeviceInterfaceDetail ensured
// DevicePath is NULL-terminated.
//
hr = StringCbCopy(DevicePath,
BufLen,
detailData->DevicePath);
LocalFree(detailData);
SetupDiDestroyDeviceInfoList(deviceInfo);
return hr;
}
I föregående funktion hämtar programmet enhetssökvägen genom att anropa dessa rutiner:
SetupDiGetClassDevs för att få en referens till enhetens informationsuppsättning, en matris som innehåller information om alla installerade enheter som matchade den angivna enhetsgränssnittsklassen GUID_DEVINTERFACE_USBApplication1. Varje element i matrisen som kallas för ett enhetsgränssnitt motsvarar en enhet som är installerad och registrerad i systemet. Enhetsgränssnittsklassen identifieras genom att skicka det GUID för enhetsgränssnittet som du definierade i INF-filen. Funktionen returnerar en HDEVINFO-referens till enhetens informationsuppsättning.
SetupDiEnumDeviceInterfaces för att räkna upp enhetsgränssnitten i enhetens informationsuppsättning och hämta information om enhetsgränssnittet.
Det här anropet kräver följande:
En initierad struktur som är anropartilldelad SP_DEVICE_INTERFACE_DATA som har sin cbSize-medlem inställd på strukturens storlek.
HDEVINFO-handtaget från steg 1.
Enhetsgränssnittets GUID som du definierade i INF-filen.
SetupDiEnumDeviceInterfaces letar upp matrisen för enhetsinformationsuppsättningen för det angivna indexet för enhetsgränssnittet och fyller den initierade SP_DEVICE_INTERFACE_DATA struktur med grundläggande data om gränssnittet.
Om du vill räkna upp alla enhetsgränssnitt i enhetens informationsuppsättning anropar du SetupDiEnumDeviceInterfaces i en loop tills funktionen returnerar FALSE och felkoden för felet ERROR_NO_MORE_ITEMS. Felkoden ERROR_NO_MORE_ITEMS kan hämtas genom att anropa GetLastError. Öka medlemsindexet med varje iteration.
Alternativt kan du anropa SetupDiEnumDeviceInfo som räknar upp enhetens informationsuppsättning och returnerar information om enhetsgränssnittselement, som anges av indexet, i en anropare allokerad SP_DEVINFO_DATA struktur. Du kan sedan skicka en referens till den här strukturen i parametern DeviceInfoData i funktionen SetupDiEnumDeviceInterfaces .
SetupDiGetDeviceInterfaceDetail för att hämta detaljerade data för enhetsgränssnittet. Informationen returneras i en SP_DEVICE_INTERFACE_DETAIL_DATA struktur. Eftersom storleken på den SP_DEVICE_INTERFACE_DETAIL_DATA strukturen varierar, anropas SetupDiGetDeviceInterfaceDetail två gånger. Det första anropet hämtar buffertstorleken som ska allokeras för SP_DEVICE_INTERFACE_DETAIL_DATA struktur. Det andra anropet fyller den allokerade bufferten med detaljerad information om gränssnittet.
- Anropar SetupDiGetDeviceInterfaceDetail med parametern DeviceInterfaceDetailData inställd på NULL. Funktionen returnerar rätt buffertstorlek i parametern requiredlength . Det här anropet misslyckas med felkoden ERROR_INSUFFICIENT_BUFFER. Den här felkoden förväntas.
- Allokerar minne för en SP_DEVICE_INTERFACE_DETAIL_DATA struktur baserat på rätt buffertstorlek som hämtas i parametern requiredlength .
- Anropar SetupDiGetDeviceInterfaceDetail igen och skickar en referens till den initierade strukturen i parametern DeviceInterfaceDetailData . När funktionen returneras fylls strukturen med detaljerad information om gränssnittet. Enhetssökvägen finns i SP_DEVICE_INTERFACE_DETAIL_DATA-strukturens DevicePath-medlem .
Skapa ett filhandtag för enheten
Se OpenDevice i device.cpp.
För att interagera med enheten behöver du ett WinUSB-gränssnittshandtag till det första (standard) gränssnittet på enheten. Mallkoden hämtar filhandtaget och WinUSB-gränssnittshandtaget och lagrar dem i DEVICE_DATA-strukturen.
HRESULT
OpenDevice(
_Out_ PDEVICE_DATA DeviceData,
_Out_opt_ PBOOL FailureDeviceNotFound
)
/*++
Routine description:
Open all needed handles to interact with the device.
If the device has multiple USB interfaces, this function grants access to
only the first interface.
If multiple devices have the same device interface GUID, there is no
guarantee of which one will be returned.
Arguments:
DeviceData - Struct filled in by this function. The caller should use the
WinusbHandle to interact with the device, and must pass the struct to
CloseDevice when finished.
FailureDeviceNotFound - TRUE when failure is returned due to no devices
found with the correct device interface (device not connected, driver
not installed, or device is disabled in Device Manager); FALSE
otherwise.
Return value:
HRESULT
--*/
{
HRESULT hr = S_OK;
BOOL bResult;
DeviceData->HandlesOpen = FALSE;
hr = RetrieveDevicePath(DeviceData->DevicePath,
sizeof(DeviceData->DevicePath),
FailureDeviceNotFound);
if (FAILED(hr)) {
return hr;
}
DeviceData->DeviceHandle = CreateFile(DeviceData->DevicePath,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
if (INVALID_HANDLE_VALUE == DeviceData->DeviceHandle) {
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
bResult = WinUsb_Initialize(DeviceData->DeviceHandle,
&DeviceData->WinusbHandle);
if (FALSE == bResult) {
hr = HRESULT_FROM_WIN32(GetLastError());
CloseHandle(DeviceData->DeviceHandle);
return hr;
}
DeviceData->HandlesOpen = TRUE;
return hr;
}
- Appen anropar CreateFile för att skapa ett filhandtag för enheten genom att ange den enhetssökväg som hämtades tidigare. Den använder flaggan FILE_FLAG_OVERLAPPED eftersom WinUSB är beroende av den här inställningen.
- Genom att använda filhandtaget för enheten skapar appen ett WinUSB-gränssnittshandtag. WinUSB-funktioner använder det här handtaget för att identifiera målenheten i stället för filhandtaget. För att hämta ett WinUSB-gränssnittshandtag anropar appen WinUsb_Initialize genom att skicka filhandtaget. Använd det mottagna handtaget i efterföljande anrop för att hämta information från enheten och för att skicka I/O-begäranden till enheten.
Släpp enhetshandtagen – se CloseDevice i device.cpp
Mallkoden implementerar kod för att frigöra filhandtaget och WinUSB-gränssnittshandtaget för enheten.
- StängHandle för att släppa handtaget som skapades av CreateFile enligt beskrivningen i avsnittet Skapa ett filhandtag för enheten i den här genomgången.
- WinUsb_Free för att frigöra WinUSB-gränssnittshandtaget för enheten, som returneras av WinUsb_Initialize .
VOID
CloseDevice(
_Inout_ PDEVICE_DATA DeviceData
)
/*++
Routine description:
Perform required cleanup when the device is no longer needed.
If OpenDevice failed, do nothing.
Arguments:
DeviceData - Struct filled in by OpenDevice
Return value:
None
--*/
{
if (FALSE == DeviceData->HandlesOpen) {
//
// Called on an uninitialized DeviceData
//
return;
}
WinUsb_Free(DeviceData->WinusbHandle);
CloseHandle(DeviceData->DeviceHandle);
DeviceData->HandlesOpen = FALSE;
return;
}
Nästa steg
Läs sedan dessa avsnitt för att hämta enhetsinformation och skicka dataöverföringar till enheten.
Få åtkomst till en USB-enhet med Hjälp av WinUSB Functions
Lär dig mer om att fråga enheten om USB-specifik information, till exempel enhetshastighet, gränssnittsbeskrivningar, relaterade slutpunkter och deras rör.
Skicka USB-isochroniska överföringar från en WinUSB-skrivbordsapp
Överföra data till och från isokrona slutpunkter för en USB-enhet.