VSCode: Enumerate USB devices

Historically, reading and writing devices to a USB port has been a level more complicated than using a serial port or parallel port.

To open a USB port (host side), first enumerate the visible devices connected to the port, and specify the relevant port (the port to which the device needs to be operated is connected) before reading and writing operations can be performed on the device connected to it. When I first came into contact with USB operation, I was really stumped.

This article mainly describes the first step (after obtaining the relevant USB port information, as for the read and write operations on it, you will need to add it at the end of this article).

One of the ways to enumerate USB devices is to use GUID. Personally, I think there is an inconvenience in applications developed using this method. It customizes the environment of the host. Once the target device is plugged into another host, Or if the host system changes, the GUID will change, causing the application to be unable to recognize the relevant target device.

The example in this article uses two pieces of information, VID and PID, to identify the device. The two pieces of information, VID and PID, are easy to obtain. For example, you can use bus hound to read the device plugged into the host, and then read the information of the device. In addition, when the equipment is purchased, it can also be obtained from the manufacturer.

Of course, when writing an application, you can use a more popular design, which is to display all visible USB devices on the host, and then select the target device. (This approach is more suitable for devices of the same type, but VIP &PID are more likely to change. Case). If the VIP & PID is fixed in the program, once the VIP & PID changes, the application program must be changed to continue operation.

The routine is as follows:

/**
 * @file main.cpp
 * @author your name ([email protected])
 * @brief
 * @version 0.1
 *
 * @copyright Copyright (c) 2023
 *
 */
#include <Windows.h>
#include <SetupAPI.h>
#include <stdio.h>

// Define the VID and PID of the target device to be enumerated
#define MY_VID 0x1234 //I’m just giving an example here
#define MY_PID 0x5678

int main()
{
    // Define variables
    HDEVINFO hDevInfo;
    SP_DEVINFO_DATA devInfoData;
    DWORD index;
    DWORD dataType;
    DWORD dataSize;
    BYTE propertyBuffer[1024];
    DWORD requiredSize;
    HANDLE hDevice;
    BOOL result;
    DWORD bytesReturned;

    //Initialize the device information set
    hDevInfo = SetupDiGetClassDevs(NULL, "USB", NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
    if (hDevInfo == INVALID_HANDLE_VALUE) {
        printf("Failed to get device information set. Error code: %d\\
", GetLastError());
        return 1;
    }

    // Enumerate all visible devices plugged into the host
    devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
    index = 0;
    while (SetupDiEnumDeviceInfo(hDevInfo, index, & amp;devInfoData)) {
        //Get the device instance ID
        result = SetupDiGetDeviceInstanceId(hDevInfo, & amp;devInfoData, propertyBuffer, sizeof(propertyBuffer), & amp;requiredSize);
        if (!result) {
            printf("Failed to get device instance ID. Error code: %d\\
", GetLastError());
            SetupDiDestroyDeviceInfoList(hDevInfo);
            return 1;
        }

        // Check whether the related device matches the specified VID and PID
        result = SetupDiGetDeviceRegistryProperty(hDevInfo, & amp;devInfoData, SPDRP_HARDWAREID, & amp;dataType, propertyBuffer, sizeof(propertyBuffer), & amp;requiredSize);
        if (result & amp; & amp; (dataType == REG_MULTI_SZ)) {
            char* ptr = (char*)propertyBuffer;
            while (*ptr) {
                if (sscanf(ptr, "USB\VID_%x & amp;PID_%x", & amp;dataType, & amp;dataSize) == 2) {
                    if ((dataType == MY_VID) & amp; & amp; (dataSize == MY_PID)) {
                        // If the matching VID &PID is successful, create a handle to read and write the device
                        hDevice = CreateFileA((LPCSTR)propertyBuffer, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
                        if (hDevice == INVALID_HANDLE_VALUE) {
                            printf("Failed to open device. Error code: %d\\
", GetLastError());
                            SetupDiDestroyDeviceInfoList(hDevInfo);
                            return 1;
                        }

                        // Send a request to the device
                        result = DeviceIoControl(hDevice, /* ... */);
                        if (!result) {
                            printf("The request to the target device failed. Error code: %d\\
", GetLastError());
                            CloseHandle(hDevice);
                            SetupDiDestroyDeviceInfoList(hDevInfo);
                            return 1;
                        }

                        // Close the device handle
                        CloseHandle(hDevice);
                    }
                }
                ptr + = strlen(ptr) + 1;
            }
        }

        index + + ;
    }

    // Clean up the device information set
    SetupDiDestroyDeviceInfoList(hDevInfo);

    return 0;
}

To add, while environment variables are not necessarily important, the tasks.json I use is:

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build",
            "type": "shell",
            "command": "\Compiler path\g + + .exe",
            "args": [
                "${fileDirname}\*.cpp",
                "-o",
                "${fileBasenameNoExtension}.exe",
                "-std=c + + 11",
                "-g"
            ],
            "presentation": {
                "echo": true,
                "reveal": "always",
                "panel": "new"
            },
            "problemMatcher": "$msCompile",
            "group": "build"
        },
        {
            "label": "run",
            "type": "shell",
            "dependsOn": "build",
            "command": "${fileDirname}\${fileBasenameNoExtension}.exe",
            "group": "build",
            "presentation": {
                "echo": true,
                "reveal": "always",
                "focus": true,
                "panel": "new"
            }
        },
        {
            "type": "cppbuild",
            "label": "C/C++ : g++ .exe build active file",
            "command": "\Compiler path\g + + .exe",
            "args": [
                "-fdiagnostics-color=always",
                "-std=c + + 11",
                "-g",
                "${fileDirname}\*.cpp",
                "-o",
                "${fileDirname}\${fileBasenameNoExtension}.exe"
            ],
            "options": {
                "cwd": "\Folder path where the compiler is placed\bin"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "detail": "Task generated by Debugger."
        }
    ]
}

[End of this article]