You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于WinAPI识别Arduino Uno R3及关联COM句柄的技术问询

Alright, let's break down your problem into two clear parts: verifying if a COM port handle connects to an Arduino, and creating a CreateFile handle from your enumerated USB devices. Here's how to solve both without relying on third-party libraries.

1. Identify Arduino from a COM Port Handle

When you have a CreateFile handle to a COM port, you can map it to the underlying device path using QueryDosDevice, then use SetupAPI functions to check device properties (like hardware IDs or friendly names) to confirm it’s an Arduino.

Implementation Code

#include <windows.h>
#include <setupapi.h>
#include <initguid.h>
#include <devguid.h>
#include <string>
#include <vector>

// Helper to get the raw device path from a COM port name
std::string GetDevicePathFromCom(const std::string& comName) {
    char devicePath[MAX_PATH] = {0};
    if (QueryDosDeviceA(comName.c_str(), devicePath, MAX_PATH)) {
        return std::string(devicePath);
    }
    return "";
}

// Check if a device path corresponds to an Arduino
bool IsArduinoDevice(const std::string& devicePath) {
    // Open device info set for COM ports
    HDEVINFO hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_COMPORT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
    if (hDevInfo == INVALID_HANDLE_VALUE) {
        return false;
    }

    SP_DEVICE_INTERFACE_DATA deviceInterfaceData = {0};
    deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);

    for (DWORD i = 0; SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_COMPORT, i, &deviceInterfaceData); i++) {
        // Get required buffer size for device details
        DWORD requiredSize = 0;
        SetupDiGetDeviceInterfaceDetailA(hDevInfo, &deviceInterfaceData, NULL, 0, &requiredSize, NULL);
        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
            continue;
        }

        // Allocate buffer and retrieve device details
        std::vector<char> detailBuffer(requiredSize);
        PSP_DEVICE_INTERFACE_DETAIL_DATA_A pDetailData = reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA_A>(detailBuffer.data());
        pDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);

        SP_DEVINFO_DATA devInfoData = {0};
        devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

        if (SetupDiGetDeviceInterfaceDetailA(hDevInfo, &deviceInterfaceData, pDetailData, requiredSize, NULL, &devInfoData)) {
            // Match the device path
            if (std::string(pDetailData->DevicePath) == devicePath) {
                // Check hardware ID (official Arduinos use VID_2341)
                char hardwareId[MAX_PATH] = {0};
                if (SetupDiGetDeviceRegistryPropertyA(hDevInfo, &devInfoData, SPDRP_HARDWAREID, NULL, reinterpret_cast<PBYTE>(hardwareId), sizeof(hardwareId), NULL)) {
                    std::string hwId(hardwareId);
                    if (hwId.find("VID_2341") != std::string::npos) {
                        SetupDiDestroyDeviceInfoList(hDevInfo);
                        return true;
                    }
                }

                // Fallback to checking friendly name
                char friendlyName[MAX_PATH] = {0};
                if (SetupDiGetDeviceRegistryPropertyA(hDevInfo, &devInfoData, SPDRP_FRIENDLYNAME, NULL, reinterpret_cast<PBYTE>(friendlyName), sizeof(friendlyName), NULL)) {
                    std::string friendly(friendlyName);
                    if (friendly.find("Arduino") != std::string::npos) {
                        SetupDiDestroyDeviceInfoList(hDevInfo);
                        return true;
                    }
                }
            }
        }
    }

    SetupDiDestroyDeviceInfoList(hDevInfo);
    return false;
}

// Usage example
void VerifyArduinoOnCom(const std::string& comName) {
    std::string devicePath = GetDevicePathFromCom(comName);
    if (!devicePath.empty() && IsArduinoDevice(devicePath)) {
        printf("COM port %s is an Arduino device!\n", comName.c_str());
    } else {
        printf("COM port %s is not an Arduino.\n", comName.c_str());
    }
}

2. Create a CreateFile Handle from Enumerated USB Devices

Your existing USB enumeration code collects device instances, but you need to map these to their associated COM ports. Here’s how to extend your code to get the COM port name and use it to open a handle.

Step 1: Update the device Struct

Add a field to store the COM port name:

struct device {
    std::string id;
    std::string busReportedDesc;
    std::string comPort; // New field for COM port name
};

Step 2: Extend the USB Enumeration Code

Modify your ListDevices function to retrieve the COM port for each USB device:

std::vector<device> usbenumerator::ListDevices() {
    DWORD dwSize;
    DEVPROPTYPE ulPropertyType;
    CONFIGRET status;
    HDEVINFO hDevInfo;
    SP_DEVINFO_DATA DeviceInfoData;
    char szDeviceInstanceID[MAX_DEVICE_ID_LEN];
    WCHAR szBuffer[4096];
    FN_SetupDiGetDeviceProperty GetDeviceProperty = (FN_SetupDiGetDeviceProperty)(GetProcAddress(GetModuleHandle("setupapi.dll"), "SetupDiGetDevicePropertyW"));

    // Initialize USB device info set (your original code)
    hDevInfo = SetupDiGetClassDevs(NULL, "USB", NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
    std::vector<device> output;
    if (hDevInfo == INVALID_HANDLE_VALUE) {
        return output;
    }

    for (int i = 0; ; i++) {
        device dev;
        DeviceInfoData.cbSize = sizeof(DeviceInfoData);
        if (!SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData)) {
            break;
        }

        // Get device instance ID (your original code)
        status = CM_Get_Device_ID(DeviceInfoData.DevInst, szDeviceInstanceID, MAX_PATH, 0);
        if (status != CR_SUCCESS) {
            continue;
        }
        dev.id = std::string(szDeviceInstanceID);

        // Get bus-reported description (your original code)
        if (GetDeviceProperty) {
            if (GetDeviceProperty(hDevInfo, &DeviceInfoData, &DEVPKEY_Device_BusReportedDeviceDesc, &ulPropertyType, (BYTE*) szBuffer, sizeof(szBuffer), &dwSize, 0)) {
                _bstr_t b(szBuffer);
                dev.busReportedDesc = std::string(static_cast<const char*>(b));
            }
        }

        // Find associated COM port for this USB device
        HDEVINFO hComDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_COMPORT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
        if (hComDevInfo != INVALID_HANDLE_VALUE) {
            SP_DEVICE_INTERFACE_DATA comInterfaceData = {0};
            comInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);

            for (DWORD j = 0; SetupDiEnumDeviceInterfaces(hComDevInfo, NULL, &GUID_DEVINTERFACE_COMPORT, j, &comInterfaceData); j++) {
                DWORD requiredSize = 0;
                SetupDiGetDeviceInterfaceDetailA(hComDevInfo, &comInterfaceData, NULL, 0, &requiredSize, NULL);
                if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
                    continue;
                }

                std::vector<char> detailBuffer(requiredSize);
                PSP_DEVICE_INTERFACE_DETAIL_DATA_A pDetailData = reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA_A>(detailBuffer.data());
                pDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);

                SP_DEVINFO_DATA comDevInfoData = {0};
                comDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

                if (SetupDiGetDeviceInterfaceDetailA(hComDevInfo, &comInterfaceData, pDetailData, requiredSize, NULL, &comDevInfoData)) {
                    // Get parent device instance ID of the COM port
                    DWORD parentDevInst;
                    status = CM_Get_Parent(&parentDevInst, comDevInfoData.DevInst, 0);
                    if (status == CR_SUCCESS) {
                        char parentID[MAX_PATH] = {0};
                        status = CM_Get_Device_ID(parentDevInst, parentID, MAX_PATH, 0);
                        if (status == CR_SUCCESS && std::string(parentID) == dev.id) {
                            // Extract COM port name from device path
                            std::string devicePath(pDetailData->DevicePath);
                            size_t comPos = devicePath.find("\\\\.\\COM");
                            if (comPos != std::string::npos) {
                                dev.comPort = devicePath.substr(comPos + 4); // Extract "COMX"
                                break;
                            }
                        }
                    }
                }
            }
            SetupDiDestroyDeviceInfoList(hComDevInfo);
        }

        output.push_back(dev);
    }

    SetupDiDestroyDeviceInfoList(hDevInfo);
    return output;
}

Step 3: Open the COM Port with CreateFile

Use the retrieved COM port name to create a handle and configure serial settings:

// Usage example: Open Arduino devices from enumerated list
void OpenArduinoDevices() {
    usbenumerator enumerator;
    std::vector<device> devices = enumerator.ListDevices();

    for (const auto& dev : devices) {
        // Check if it's an Arduino (match bus description or VID)
        if (!dev.comPort.empty() && 
            (dev.busReportedDesc.find("Arduino") != std::string::npos || dev.id.find("VID_2341") != std::string::npos)) {
            
            std::string comPath = "\\\\.\\" + dev.comPort; // Required prefix for CreateFile
            HANDLE hSerial = CreateFile(comPath.c_str(), 
                                        GENERIC_READ | GENERIC_WRITE, 
                                        0, 
                                        NULL, 
                                        OPEN_EXISTING, 
                                        FILE_ATTRIBUTE_NORMAL, 
                                        NULL);

            if (hSerial != INVALID_HANDLE_VALUE) {
                printf("Successfully opened Arduino on %s\n", dev.comPort.c_str());

                // Configure serial port to match Arduino's settings (default 9600 baud)
                DCB dcbSerialParams = {0};
                dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
                if (GetCommState(hSerial, &dcbSerialParams)) {
                    dcbSerialParams.BaudRate = CBR_9600;
                    dcbSerialParams.ByteSize = 8;
                    dcbSerialParams.StopBits = ONESTOPBIT;
                    dcbSerialParams.Parity = NOPARITY;
                    SetCommState(hSerial, &dcbSerialParams);
                }

                // Remember to close the handle when done:
                // CloseHandle(hSerial);
            } else {
                printf("Failed to open %s: Error %d\n", dev.comPort.c_str(), GetLastError());
            }
        }
    }
}

Key Notes

  • Hardware IDs: Official Arduino boards use VID 2341; clones may use other VIDs (like 1A86 for CH340-based boards). Adjust the checks to include these if needed.
  • Serial Configuration: Always match the serial parameters (baud rate, parity, etc.) to your Arduino’s Serial.begin() call.
  • Error Handling: The code includes basic error handling, but you should expand it for production use (e.g., logging detailed errors from GetLastError()).

内容的提问来源于stack exchange,提问作者Tefek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:30:56