|
串口编程 - windows如何枚举串口|获取活跃串口号 环境:
OS: windows 10
compiler: VS2019 前言进行串口通信,首先会面临串口枚举的问题。本文将介绍windows如何枚举串口。 1. windows枚举设备简介一般而言,windows下枚举的串口可以分为如下几种类型: - 物理串口:RS232
- 虚拟串口:com0com 或 ELTIMA Virtual Serial Port 等
- 蓝牙转串口
- USB转串口
- STM32虚拟COM端口 (设备管理器显示 STMicroelectronics Virtual COM Port)(未测试)
2. windows枚举串口代码实现以下以三种主流方式实现枚举串口,包括CreateFile函数遍历、注册表和setupapi三种方式。 串口友好名称物理设备对象名称COM1ELTIMA Virtual Serial Port\Device\VSerial7_0COM2ELTIMA Virtual Serial Port\Device\VSerial7_1COM4Prolific USB-to-Serial Comm Port\Device\ProlificSerial0COM5Standard Serial over Bluetooth link\Device\BthModem0COM6com0com - serial port emulator\Device\com0com11COM7com0com - serial port emulator\Device\com0com21CNCA0com0com - serial port emulator CNCA0\Device\com0com10CNCB0com0com - serial port emulator CNCB0\Device\com0com20
串口 友好名称COM1 ELTIMA Virtual Serial PortCOM2 ELTIMA Virtual Serial PortCOM4 Prolific USB-to-Serial Comm PortCOM5 Standard Serial over Bluetooth linkCOM6 com0com - serial port emulatoCOM7 com0com - serial port emulatorCNCA0 com0com - serial port emulator CNCB0 com0com - serial port emulator CNCB0测试结果: 0 - COM11 - COM22 - COM43 - COM54 - COM65 - COM7time : 375 ms0 - CNCA01 - CNCB02 - COM13 - COM24 - COM55 - COM66 - COM77 - COM4time : 0 m0 - COM6 com0com - serial port emulator1 - COM4 Prolific USB-to-Serial Comm Port2 - COM1 ELTIMA Virtual Serial Port3 - CNCA0 com0com - serial port emulator CNCA0 (CNCA0)4 - COM7 com0com - serial port emulator5 - COM5 Standard Serial over Bluetooth link6 - COM2 ELTIMA Virtual Serial Port7 - CNCB0 com0com - serial port emulator CNCB0 (CNCB0)time : 0 ms2.1 CreateFile函数遍历使用CreateFile函数遍历COM1-COM255,如果返回结果正确或错误为被占用,则认为是活跃串口。但是该方式只能针对串口名称为COMxxx格式的串口。 代码: #include <iostream>#include <string>#include <vector>#include "windows.h" // CreateFile GetTickCount64#include "tchar.h" // _sntprintf _T using namespace std;struct SerialPortInfo{ std::string portName; std::string description;};std::string wstringToString(const std::wstring& wstr){ // https://stackoverflow.com/questi ... wstring-into-string if (wstr.empty()) { return std::string(); } int size = WideCharToMultiByte(CP_ACP, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL); std::string ret = std::string(size, 0); WideCharToMultiByte(CP_ACP, 0, &wstr[0], (int)wstr.size(), &ret[0], size, NULL, NULL); // CP_UTF8 return ret;}bool enumDetailsSerialPorts(vector<SerialPortInfo>& portInfoList){ bool bRet = false; std::string strPortName; HANDLE m_handle; TCHAR portName[255] = { 0 }; SerialPortInfo m_serialPortInfo; for (int i = 1; i <= 255; i++) { bRet = false; _sntprintf(portName, sizeof(portName), _T("\\\\.\\COM%d"), i); m_handle = CreateFile(portName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); if (m_handle == INVALID_HANDLE_VALUE) { if (ERROR_ACCESS_DENIED == GetLastError()) { bRet = true; } } else { bRet = true; } if (bRet) {#ifdef UNICODE strPortName = wstringToString(portName);#else strPortName = std::string(portName);#endif m_serialPortInfo.portName = strPortName; portInfoList.push_back(m_serialPortInfo); } CloseHandle(m_handle); } return bRet;}int main(){ ULONGLONG startTick = 0; startTick = GetTickCount64(); vector<SerialPortInfo> spInfo; enumDetailsSerialPorts(spInfo); for (int i = 0; i < spInfo.size(); i++) { std::cout << i << " - " << spInfo.portName << " " << spInfo.description << std::endl; } _tprintf(_T("time : %I64u ms"), GetTickCount64() - startTick); return 0;}2.2 注册表注册表HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM查询,可以实时获取活跃串口 代码: #include <iostream>#include <string>#include <vector>#include "windows.h" // CreateFile GetTickCount64#include "tchar.h" // _sntprintf _T using namespace std;struct SerialPortInfo{ std::string portName; std::string description;};std::string wstringToString(const std::wstring& wstr){ // https://stackoverflow.com/questi ... wstring-into-string if (wstr.empty()) { return std::string(); } int size = WideCharToMultiByte(CP_ACP, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL); std::string ret = std::string(size, 0); WideCharToMultiByte(CP_ACP, 0, &wstr[0], (int)wstr.size(), &ret[0], size, NULL, NULL); // CP_UTF8 return ret;}bool enumDetailsSerialPorts(vector<SerialPortInfo>& portInfoList){ //https://msdn.microsoft.com/en-us/library/ms724256#define MAX_KEY_LENGTH 255#define MAX_VALUE_NAME 16383 HKEY hKey; TCHAR achValue[MAX_VALUE_NAME]; // buffer for subkey name DWORD cchValue = MAX_VALUE_NAME; // size of name string TCHAR achClass[MAX_PATH] = _T(""); // buffer for class name DWORD cchClassName = MAX_PATH; // size of class string DWORD cSubKeys = 0; // number of subkeys DWORD cbMaxSubKey; // longest subkey size DWORD cchMaxClass; // longest class string DWORD cKeyNum; // number of values for key DWORD cchMaxValue; // longest value name DWORD cbMaxValueData; // longest value data DWORD cbSecurityDescriptor; // size of security descriptor FILETIME ftLastWriteTime; // last write time int iRet = -1; bool bRet = false; std::string strPortName; SerialPortInfo m_serialPortInfo; TCHAR strDSName[MAX_VALUE_NAME]; memset(strDSName, 0, MAX_VALUE_NAME); DWORD nValueType = 0; DWORD nBuffLen = 10; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DEVICEMAP\\SERIALCOMM"), 0, KEY_READ, &hKey)) { // Get the class name and the value count. iRet = RegQueryInfoKey( hKey, // key handle achClass, // buffer for class name &cchClassName, // size of class string NULL, // reserved &cSubKeys, // number of subkeys &cbMaxSubKey, // longest subkey size &cchMaxClass, // longest class string &cKeyNum, // number of values for this key &cchMaxValue, // longest value name &cbMaxValueData, // longest value data &cbSecurityDescriptor, // security descriptor &ftLastWriteTime); // last write time if (!portInfoList.empty()) { portInfoList.clear(); } // Enumerate the key values. if (cKeyNum > 0 && ERROR_SUCCESS == iRet) { for (int i = 0; i < (int)cKeyNum; i++) { cchValue = MAX_VALUE_NAME; achValue[0] = '\0'; nBuffLen = MAX_KEY_LENGTH;//防止 ERROR_MORE_DATA 234L 错误 if (ERROR_SUCCESS == RegEnumValue(hKey, i, achValue, &cchValue, NULL, NULL, (LPBYTE)strDSName, &nBuffLen)) {#ifdef UNICODE strPortName = wstringToString(strDSName);#else strPortName = std::string(strDSName);#endif m_serialPortInfo.portName = strPortName; portInfoList.push_back(m_serialPortInfo); } } } else { } } if (portInfoList.empty()) { bRet = false; } else { bRet = true; } RegCloseKey(hKey); return bRet;}int main(){ ULONGLONG startTick = 0; startTick = GetTickCount64(); vector<SerialPortInfo> spInfo; enumDetailsSerialPorts(spInfo); for (int i = 0; i < spInfo.size(); i++) { std::cout << i << " - " << spInfo.portName << " " << spInfo.description << std::endl; } _tprintf(_T("time : %I64u ms"), GetTickCount64() - startTick); return 0;}2.3 setupapi(GUID_DEVINTERFACE_COMPORT && SetupDiEnumDeviceInfo)setupapi可以遍历设备管理器,因此可以用来枚举串口(包括友好名称、物理设备对象名称等),其本质上也是操作注册表。 代码: // https://github.com/itas109/CSeri ... PortInfoWinBase.cpp#include <iostream>#include <string>#include <vector>#include "windows.h" // CreateFile GetTickCount64#include "tchar.h" // _sntprintf _T #include <Setupapi.h> //SetupDiGetClassDevs Setup*#include <initguid.h> //GUID#pragma comment (lib, "setupapi.lib")using namespace std;#ifndef GUID_DEVINTERFACE_COMPORTDEFINE_GUID(GUID_DEVINTERFACE_COMPORT, 0x86E0D1E0L, 0x8089, 0x11D0, 0x9C, 0xE4, 0x08, 0x00, 0x3E, 0x30, 0x1F, 0x73);#endifstruct SerialPortInfo{ std::string portName; std::string description;};std::string wstringToString(const std::wstring& wstr){ // https://stackoverflow.com/questi ... wstring-into-string if (wstr.empty()) { return std::string(); } int size = WideCharToMultiByte(CP_ACP, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL); std::string ret = std::string(size, 0); WideCharToMultiByte(CP_ACP, 0, &wstr[0], (int)wstr.size(), &ret[0], size, NULL, NULL); // CP_UTF8 return ret;}bool enumDetailsSerialPorts(vector<SerialPortInfo>& portInfoList){ // https://docs.microsoft.com/en-us ... tupdienumdeviceinfo bool bRet = false; SerialPortInfo m_serialPortInfo; std::string strFriendlyName; std::string strPortName; HDEVINFO hDevInfo = INVALID_HANDLE_VALUE; // Return only devices that are currently present in a system // The GUID_DEVINTERFACE_COMPORT device interface class is defined for COM ports. GUID // {86E0D1E0-8089-11D0-9CE4-08003E301F73} hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_COMPORT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (INVALID_HANDLE_VALUE != hDevInfo) { SP_DEVINFO_DATA devInfoData; // The caller must set DeviceInfoData.cbSize to sizeof(SP_DEVINFO_DATA) devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); for (DWORD i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &devInfoData); i++) { // get port name TCHAR portName[256]; HKEY hDevKey = SetupDiOpenDevRegKey(hDevInfo, &devInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); if (INVALID_HANDLE_VALUE != hDevKey) { DWORD dwCount = 255; // DEV_NAME_MAX_LEN RegQueryValueEx(hDevKey, _T("PortName"), NULL, NULL, (BYTE*)portName, &dwCount); RegCloseKey(hDevKey); } // get friendly name TCHAR fname[256]; SetupDiGetDeviceRegistryProperty(hDevInfo, &devInfoData, SPDRP_FRIENDLYNAME, NULL, (PBYTE)fname, sizeof(fname), NULL);#ifdef UNICODE strPortName = wstringToString(portName); strFriendlyName = wstringToString(fname);#else strPortName = std::string(portName); strFriendlyName = std::string(fname);#endif // remove (COMxx) strFriendlyName = strFriendlyName.substr(0, strFriendlyName.find(("(COM"))); m_serialPortInfo.portName = strPortName; m_serialPortInfo.description = strFriendlyName; portInfoList.push_back(m_serialPortInfo); } if (ERROR_NO_MORE_ITEMS == GetLastError()) { bRet = true; // no more item } } SetupDiDestroyDeviceInfoList(hDevInfo); return bRet;}int main(){ ULONGLONG startTick = 0; startTick = GetTickCount64(); vector<SerialPortInfo> spInfo; enumDetailsSerialPorts(spInfo); for (int i = 0; i < spInfo.size(); i++) { std::cout << i << " - " << spInfo.portName << " " << spInfo.description << std::endl; } _tprintf(_T("time : %I64u ms"), GetTickCount64() - startTick); return 0;}
Refrence: - https://github.com/itas109/CSerialPort
- http://www.naughter.com/enumser.html
|