前言
接到一个临时任务,写一个串行口小工具连续读写某设备,而设备还未准备好。于是,安装了一下VSPD准备调试,虚拟了一对串行口COM30和COM31,用之前在codeproject上拷贝的方法(基于SetupDiGetClassDevs和SetupDiEnumDeviceInterfaces函数)扫描时,居然扫描不到。
在网路上游荡,幸运地发现了 naughter 实现了各种方法的扫描,于是,有了本文。
CEnumerateSerial
作者: naughter
博客: http://www.naughter.com/enumser.html
发布: v1.37 (15 January 2019)
CEnumerateSerial v1.37 A C++ class to enumerate serial ports
One of the posts that keeps reappearing in the programming newsgroups is how to enumerate all the serial ports installed. The code uses a number of different methods to enumerate the ports.
PS: 以下源代码并非CEnumerateSerial v1.37,而是转载自如下地址(够用就好,懒得查看是转载的哪个版本了,知道出处是naughter的且无版权障碍就行)
作者:垓恪十三號
来源:CSDN
博文:https://blog.csdn.net/u013606170/article/details/46903713
头文件
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <setupapi.h>
#include <locale.h>
#define MAX_PORT_NUM (256)
#define MAX_STR_LEN (256*sizeof(TCHAR))
EnumerateComPortByCreateFile
/*assure portName be double ARRAY , not double point*/
BOOL EnumerateComPortByCreateFile(UINT *pNumber, TCHAR *pPortName, int strMaxLen)
{
UINT i, jj;
INT ret;
TCHAR *pTempPortName;
*pNumber = 0;
jj = 0;
pTempPortName = (TCHAR*)HeapAlloc(GetProcessHeap(),
HEAP_GENERATE_EXCEPTIONS|HEAP_ZERO_MEMORY,
strMaxLen*sizeof(pTempPortName));
ret = FALSE;
for (i = 1; i<= 255; i++){
HANDLE hSerial;
_stprintf_s(pTempPortName, strMaxLen, TEXT("\\\\.\\COM%u"), i);
hSerial = CreateFile(pTempPortName, GENERIC_READ | GENERIC_WRITE,
0, 0, OPEN_EXISTING, 0, 0);
if(INVALID_HANDLE_VALUE == hSerial)
continue;
_tcsncpy(pPortName + jj*strMaxLen, pTempPortName,
_tcsnlen(pTempPortName, strMaxLen));
jj++;
}/*for [i MAX_PORT_NUM] */
HeapFree(GetProcessHeap(), 0, pTempPortName); pTempPortName = NULL;
*pNumber = jj;
if(0 <jj)
ret = TRUE;
return ret;
}/*EnumerateComPortByCreateFile*/
EnumerateComPortQueryDosDevice
BOOL EnumerateComPortQueryDosDevice(UINT *pNumber, TCHAR *pPortName, int strMaxLen)
{
UINT i, jj;
INT ret;
OSVERSIONINFOEX osvi;
ULONGLONG dwlConditionMask;
DWORD dwChars;
TCHAR *pDevices;
UINT nChars;
ret = FALSE;
memset(&osvi, 0, sizeof(osvi));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
osvi.dwPlatformId = VER_PLATFORM_WIN32_NT;
dwlConditionMask = 0;
VER_SET_CONDITION(dwlConditionMask, VER_PLATFORMID, VER_EQUAL);
if(FALSE == VerifyVersionInfo(&osvi, VER_PLATFORMID, dwlConditionMask))
{
DWORD dwError = GetLastError();
_tprintf(TEXT("VerifyVersionInfo error, %d\n", dwError));
return -1;
}/*if*/
pDevices = NULL;
nChars = 4096;
pDevices = (TCHAR*)HeapAlloc(GetProcessHeap(),
HEAP_GENERATE_EXCEPTIONS, nChars*sizeof(TCHAR));
while(0 < nChars)
{
dwChars = QueryDosDevice(NULL, pDevices, nChars);
if(0 == dwChars)
{
DWORD dwError = GetLastError();
if(ERROR_INSUFFICIENT_BUFFER == dwError)
{
nChars *= 2;
HeapFree(GetProcessHeap(), 0, pDevices);
pDevices = (TCHAR*)HeapAlloc(GetProcessHeap(),
HEAP_GENERATE_EXCEPTIONS, nChars*sizeof(TCHAR));
continue;
}/*if ERROR_INSUFFICIENT_BUFFER == dwError*/
_tprintf(TEXT("QueryDosDevice error, %d\n", dwError));
return -1;
}/*if */
//printf("dwChars = %d\n", dwChars);
i = 0;
jj = 0;
while (TEXT('\0') != pDevices[i] )
{
TCHAR* pszCurrentDevice;
size_t nLen;
pszCurrentDevice = &(pDevices[i]);
nLen = _tcslen(pszCurrentDevice);
//_tprintf(TEXT("%s\n"), &pTargetPathStr[i]);
if (3 < nLen)
{
if ((0 == _tcsnicmp(pszCurrentDevice, TEXT("COM"), 3))
&& FALSE != isdigit(pszCurrentDevice[3]) )
{
//Work out the port number
_tcsncpy(pPortName + jj*strMaxLen,
pszCurrentDevice, MAX_STR_LEN);
jj++;
}
}
i += (nLen + 1);
}
break;
}/*while*/
if(NULL != pDevices)
HeapFree(GetProcessHeap(), 0, pDevices);
*pNumber = jj;
if(0 < jj)
ret = TRUE;
return ret;
}/*EnumerateComPortByQueryDosDevice*/
EnumerateComPortByGetDefaultCommConfig
BOOL EnumerateComPortByGetDefaultCommConfig(UINT *pNumber, TCHAR *pPortName,
int strMaxLen)
{
UINT i, jj;
INT ret;
TCHAR *pTempPortName;
pTempPortName = (TCHAR*)HeapAlloc(GetProcessHeap(),
HEAP_GENERATE_EXCEPTIONS| HEAP_ZERO_MEMORY, strMaxLen);
*pNumber = 0;
jj = 0;
ret = FALSE;
for (i = 1; i<=255; i++){
//Form the Raw device name
COMMCONFIG cc;
DWORD dwSize ;
dwSize = sizeof(COMMCONFIG);
_stprintf_s(pTempPortName, strMaxLen/2, TEXT("COM%u"), i);
if (FALSE == GetDefaultCommConfig(pTempPortName, &cc, &dwSize))
continue;
_tcsncpy(pPortName + jj*strMaxLen, pTempPortName,
_tcsnlen(pTempPortName, strMaxLen));
jj++;
}/*for [1 255] */
HeapFree(GetProcessHeap(), 0, pTempPortName);
pTempPortName = NULL;
*pNumber = jj;
if(0 <jj)
ret = TRUE;
return ret;
}/*EnumerateComPortByGetDefaultCommConfig*/
EnumerateComPortSetupAPI_GUID_DEVINTERFACE_COMPORT
BOOL EnumerateComPortSetupAPI_GUID_DEVINTERFACE_COMPORT(UINT *pNumber,
TCHAR *pPortName, int strMaxLen, TCHAR *pFriendName)
{
UINT i, jj;
INT ret;
TCHAR *pTempPortName;
HMODULE hLibrary;
TCHAR szFullPath[_MAX_PATH];
GUID guid;
HDEVINFO hDevInfoSet;
typedef HKEY (__stdcall SetupDiOpenDevRegKeyFunType)
(HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM);
//typedef BOOL (__stdcall SetupDiClassGuidsFromNameFunType)
// (LPCTSTR, LPGUID, DWORD, PDWORD);
typedef BOOL (__stdcall SetupDiDestroyDeviceInfoListFunType)
(HDEVINFO);
typedef BOOL (__stdcall SetupDiEnumDeviceInfoFunType)
(HDEVINFO, DWORD, PSP_DEVINFO_DATA);
typedef HDEVINFO (__stdcall SetupDiGetClassDevsFunType)
(LPGUID, LPCTSTR, HWND, DWORD);
typedef BOOL (__stdcall SetupDiGetDeviceRegistryPropertyFunType)
(HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD);
SetupDiOpenDevRegKeyFunType* SetupDiOpenDevRegKeyFunPtr;
SetupDiGetClassDevsFunType *SetupDiGetClassDevsFunPtr;
SetupDiGetDeviceRegistryPropertyFunType *SetupDiGetDeviceRegistryPropertyFunPtr;
SetupDiDestroyDeviceInfoListFunType *SetupDiDestroyDeviceInfoListFunPtr;
SetupDiEnumDeviceInfoFunType *SetupDiEnumDeviceInfoFunPtr;
BOOL bMoreItems;
SP_DEVINFO_DATA devInfo;
ret = FALSE;
jj = 0;
szFullPath[0] = _T('\0');
//Get the Windows System32 directory
if(0 == GetSystemDirectory(szFullPath, _countof(szFullPath)))
{
_tprintf(TEXT("CEnumerateSerial::UsingSetupAPI1 failed, Error:%u\n"),
GetLastError());
return FALSE;
}/*if*/
//Setup the full path and delegate to LoadLibrary
#pragma warning(suppress: 6102) //There is a bug with the SAL annotation of GetSystemDirectory in the Windows 8.1 SDK
_tcscat_s(szFullPath, _countof(szFullPath), _T("\\"));
_tcscat_s(szFullPath, _countof(szFullPath), TEXT("SETUPAPI.DLL"));
hLibrary = LoadLibrary(szFullPath);
SetupDiOpenDevRegKeyFunPtr =
(SetupDiOpenDevRegKeyFunType*)GetProcAddress(hLibrary, "SetupDiOpenDevRegKey");
#if defined _UNICODE
SetupDiGetClassDevsFunPtr =
(SetupDiGetClassDevsFunType*)GetProcAddress(hLibrary, "SetupDiGetClassDevsW");
SetupDiGetDeviceRegistryPropertyFunPtr = (SetupDiGetDeviceRegistryPropertyFunType*)
GetProcAddress(hLibrary, "SetupDiGetDeviceRegistryPropertyW");
#else
SetupDiGetClassDevsFunPtr =
(SetupDiGetClassDevsFunType*)GetProcAddress(hLibrary, "SetupDiGetClassDevsA");
SetupDiGetDeviceRegistryPropertyFunPtr = (SetupDiGetDeviceRegistryPropertyFunType*)
GetProcAddress(hLibrary, "SetupDiGetDeviceRegistryPropertyA");
#endif
SetupDiDestroyDeviceInfoListFunPtr = (SetupDiDestroyDeviceInfoListFunType*)
GetProcAddress(hLibrary, "SetupDiDestroyDeviceInfoList");
SetupDiEnumDeviceInfoFunPtr = (SetupDiEnumDeviceInfoFunType*)
GetProcAddress(hLibrary, "SetupDiEnumDeviceInfo");
guid = GUID_DEVINTERFACE_COMPORT;
hDevInfoSet = SetupDiGetClassDevsFunPtr(&guid, NULL, NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (INVALID_HANDLE_VALUE == hDevInfoSet)
{
//Set the error to report
_tprintf(TEXT("error lpfnSETUPDIGETCLASSDEVS, %d"), GetLastError());
return FALSE;
}/*if */
//bMoreItems = TRUE;
devInfo.cbSize = sizeof(SP_DEVINFO_DATA);
i = 0;
jj = 0;
do
{
HKEY hDeviceKey;
BOOL isFound;
isFound = FALSE;
bMoreItems = SetupDiEnumDeviceInfoFunPtr(hDevInfoSet, i, &devInfo);
if(FALSE == bMoreItems)
break;
i++;
hDeviceKey = SetupDiOpenDevRegKeyFunPtr(hDevInfoSet, &devInfo,
DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
if (INVALID_HANDLE_VALUE != hDeviceKey)
{
int nPort;
size_t nLen;
LPTSTR pszPortName;
nPort = 0;
pszPortName = NULL;
{
//First query for the size of the registry value
DWORD dwType;
DWORD dwDataSize;
LONG err;
DWORD dwAllocatedSize;
DWORD dwReturnedSize;
dwType = 0; dwDataSize = 0;
err = RegQueryValueEx(hDeviceKey, TEXT("PortName"), NULL,
&dwType, NULL, &dwDataSize);
if (ERROR_SUCCESS != err)
continue;
//Ensure the value is a string
if (dwType != REG_SZ)
continue;
//Allocate enough bytes for the return value
dwAllocatedSize = dwDataSize + sizeof(TCHAR);
/* +sizeof(TCHAR) is to allow us to NULL terminate
the data if it is not null terminated in the registry
*/
pszPortName = (LPTSTR)LocalAlloc(LMEM_FIXED, dwAllocatedSize);
if (pszPortName == NULL)
continue;
//Recall RegQueryValueEx to return the data
pszPortName[0] = _T('\0');
dwReturnedSize = dwAllocatedSize;
err = RegQueryValueEx(hDeviceKey, TEXT("PortName"), NULL,
&dwType, (LPBYTE)pszPortName, &dwReturnedSize);
if (ERROR_SUCCESS != err)
{
LocalFree(pszPortName);
pszPortName = NULL;
continue;
}
//Handle the case where the data just returned is the same size as the allocated size. This could occur where the data
//has been updated in the registry with a non null terminator between the two calls to ReqQueryValueEx above. Rather than
//return a potentially non-null terminated block of data, just fail the method call
if (dwReturnedSize >= dwAllocatedSize)
continue;
//NULL terminate the data if it was not returned NULL terminated because it is not stored null terminated in the registry
if (pszPortName[dwReturnedSize/sizeof(TCHAR) - 1] != _T('\0'))
pszPortName[dwReturnedSize/sizeof(TCHAR)] = _T('\0');
}/*local varable*/
//If it looks like "COMX" then
//add it to the array which will be returned
nLen = _tcslen(pszPortName);
if (3 < nLen)
{
if (0 == _tcsnicmp(pszPortName, TEXT("COM"), 3))
{
if(FALSE == isdigit(pszPortName[3]) )
continue;
//Work out the port number
_tcsncpy(pPortName + jj*strMaxLen, pszPortName,
_tcsnlen(pszPortName, strMaxLen));
//_stprintf_s(&portName[jj][0], strMaxLen, TEXT("%s"), pszPortName);
} else
{
continue;
}/*if 0 == _tcsnicmp(pszPortName, TEXT("COM"), 3)*/
}/*if 3 < nLen*/
LocalFree(pszPortName);
isFound = TRUE;
//Close the key now that we are finished with it
RegCloseKey(hDeviceKey);
}/*INVALID_HANDLE_VALUE != hDeviceKey*/
if(FALSE == isFound)
continue;
//If the port was a serial port, then also try to get its friendly name
{
TCHAR szFriendlyName[1024];
DWORD dwSize;
DWORD dwType;
szFriendlyName[0] = _T('\0');
dwSize = sizeof(szFriendlyName);
dwType = 0;
if( (TRUE == SetupDiGetDeviceRegistryPropertyFunPtr(hDevInfoSet, &devInfo,
SPDRP_DEVICEDESC, &dwType, (PBYTE)(szFriendlyName),
dwSize, &dwSize) ) && (REG_SZ == dwType)
)
{
_tcsncpy(pFriendName + jj*strMaxLen, &szFriendlyName[0],
_tcsnlen(&szFriendlyName[0], strMaxLen));
}
else
{
_stprintf_s(pFriendName + jj*strMaxLen, strMaxLen, TEXT(""));
}/*if SetupDiGetDeviceRegistryPropertyFunPtr */
}/*local variable */
jj++;
}while(1);
*pNumber = jj;
if(0 <jj)
ret = TRUE;
return ret;
}/*EnumerateComPortSetupAPI_GUID_DEVINTERFACE_COMPORT*/
EnumerateComPortSetupAPISetupDiClassGuidsFromNamePort
BOOL EnumerateComPortSetupAPISetupDiClassGuidsFromNamePort(UINT *pNumber,
TCHAR *pPortName, int strMaxLen, TCHAR *pFriendName)
{
UINT i, jj;
INT ret;
TCHAR *pTempPortName;
HMODULE hLibrary;
TCHAR szFullPath[_MAX_PATH];
GUID *pGuid;
DWORD dwGuids;
HDEVINFO hDevInfoSet;
typedef HKEY (__stdcall SetupDiOpenDevRegKeyFunType)
(HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM);
typedef BOOL (__stdcall SetupDiClassGuidsFromNameFunType)
(LPCTSTR, LPGUID, DWORD, PDWORD);
typedef BOOL (__stdcall SetupDiDestroyDeviceInfoListFunType)
(HDEVINFO);
typedef BOOL (__stdcall SetupDiEnumDeviceInfoFunType)
(HDEVINFO, DWORD, PSP_DEVINFO_DATA);
typedef HDEVINFO (__stdcall SetupDiGetClassDevsFunType)
(LPGUID, LPCTSTR, HWND, DWORD);
typedef BOOL (__stdcall SetupDiGetDeviceRegistryPropertyFunType)
(HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD);
SetupDiOpenDevRegKeyFunType* SetupDiOpenDevRegKeyFunPtr;
SetupDiClassGuidsFromNameFunType *SetupDiClassGuidsFromNameFunPtr;
SetupDiGetClassDevsFunType *SetupDiGetClassDevsFunPtr;
SetupDiGetDeviceRegistryPropertyFunType *SetupDiGetDeviceRegistryPropertyFunPtr;
SetupDiDestroyDeviceInfoListFunType *SetupDiDestroyDeviceInfoListFunPtr;
SetupDiEnumDeviceInfoFunType *SetupDiEnumDeviceInfoFunPtr;
BOOL bMoreItems;
SP_DEVINFO_DATA devInfo;
ret = FALSE;
jj = 0;
szFullPath[0] = _T('\0');
//Get the Windows System32 directory
if(0 == GetSystemDirectory(szFullPath, _countof(szFullPath)))
{
_tprintf(TEXT("CEnumerateSerial::UsingSetupAPI1 failed, Error:%u\n"),
GetLastError());
return FALSE;
}/*if*/
//Setup the full path and delegate to LoadLibrary
#pragma warning(suppress: 6102) //There is a bug with the SAL annotation of GetSystemDirectory in the Windows 8.1 SDK
_tcscat_s(szFullPath, _countof(szFullPath), _T("\\"));
_tcscat_s(szFullPath, _countof(szFullPath), TEXT("SETUPAPI.DLL"));
hLibrary = LoadLibrary(szFullPath);
SetupDiOpenDevRegKeyFunPtr =
(SetupDiOpenDevRegKeyFunType*)GetProcAddress(hLibrary, "SetupDiOpenDevRegKey");
#if defined _UNICODE
SetupDiClassGuidsFromNameFunPtr = (SetupDiClassGuidsFromNameFunType*)
GetProcAddress(hLibrary, "SetupDiGetDeviceRegistryPropertyW");
SetupDiGetClassDevsFunPtr =
(SetupDiGetClassDevsFunType*)GetProcAddress(hLibrary, "SetupDiGetClassDevsW");
SetupDiGetDeviceRegistryPropertyFunPtr
= (SetupDiGetDeviceRegistryPropertyFunType*)GetProcAddress(hLibrary, "SetupDiGetDeviceRegistryPropertyW");
#else
SetupDiClassGuidsFromNameFunPtr = (SetupDiClassGuidsFromNameFunType*)
GetProcAddress(hLibrary, "SetupDiClassGuidsFromNameA");
SetupDiGetClassDevsFunPtr = (SetupDiGetClassDevsFunType*)
GetProcAddress(hLibrary, "SetupDiGetClassDevsA");
SetupDiGetDeviceRegistryPropertyFunPtr = (SetupDiGetDeviceRegistryPropertyFunType*)
GetProcAddress(hLibrary, "SetupDiGetDeviceRegistryPropertyA");
#endif
SetupDiDestroyDeviceInfoListFunPtr = (SetupDiDestroyDeviceInfoListFunType*)
GetProcAddress(hLibrary, "SetupDiDestroyDeviceInfoList");
SetupDiEnumDeviceInfoFunPtr = (SetupDiEnumDeviceInfoFunType*)
GetProcAddress(hLibrary, "SetupDiEnumDeviceInfo");
//First need to convert the name "Ports" to a GUID using SetupDiClassGuidsFromName
dwGuids = 0;
SetupDiClassGuidsFromNameFunPtr(TEXT("Ports"), NULL, 0, &dwGuids);
if(0 == dwGuids)
return FALSE;
//Allocate the needed memory
pGuid = (GUID*)HeapAlloc(GetProcessHeap(),
HEAP_GENERATE_EXCEPTIONS, dwGuids * sizeof(GUID));
if(NULL == pGuid)
return FALSE;
//Call the function again
if (FALSE == SetupDiClassGuidsFromNameFunPtr(TEXT("Ports"),
pGuid, dwGuids, &dwGuids))
{
return FALSE;
}/*if*/
hDevInfoSet = SetupDiGetClassDevsFunPtr(pGuid, NULL, NULL,
DIGCF_PRESENT /*| DIGCF_DEVICEINTERFACE*/);
if (INVALID_HANDLE_VALUE == hDevInfoSet)
{
//Set the error to report
_tprintf(TEXT("error SetupDiGetClassDevsFunPtr, %d"), GetLastError());
return FALSE;
}/*if */
//bMoreItems = TRUE;
devInfo.cbSize = sizeof(SP_DEVINFO_DATA);
i = 0;
jj = 0;
do
{
HKEY hDeviceKey;
BOOL isFound;
isFound = FALSE;
bMoreItems = SetupDiEnumDeviceInfoFunPtr(hDevInfoSet, i, &devInfo);
if(FALSE == bMoreItems)
break;
i++;
hDeviceKey = SetupDiOpenDevRegKeyFunPtr(hDevInfoSet, &devInfo,
DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
if (INVALID_HANDLE_VALUE != hDeviceKey)
{
int nPort;
size_t nLen;
LPTSTR pszPortName;
nPort = 0;
pszPortName = NULL;
{
//First query for the size of the registry value
DWORD dwType;
DWORD dwDataSize;
LONG err;
DWORD dwAllocatedSize;
DWORD dwReturnedSize;
dwType = 0; dwDataSize = 0;
err = RegQueryValueEx(hDeviceKey, TEXT("PortName"), NULL,
&dwType, NULL, &dwDataSize);
if (ERROR_SUCCESS != err)
continue;
//Ensure the value is a string
if (dwType != REG_SZ)
continue;
//Allocate enough bytes for the return value
dwAllocatedSize = dwDataSize + sizeof(TCHAR);
/* +sizeof(TCHAR) is to allow us to NULL terminate
the data if it is not null terminated in the registry
*/
pszPortName = (LPTSTR)LocalAlloc(LMEM_FIXED, dwAllocatedSize);
if (pszPortName == NULL)
continue;
//Recall RegQueryValueEx to return the data
pszPortName[0] = TEXT('\0');
dwReturnedSize = dwAllocatedSize;
err = RegQueryValueEx(hDeviceKey, TEXT("PortName"), NULL,
&dwType, (LPBYTE)pszPortName, &dwReturnedSize);
if (ERROR_SUCCESS != err)
{
LocalFree(pszPortName);
pszPortName = NULL;
continue;
}
//Handle the case where the data just returned is the same size as the allocated size. This could occur where the data
//has been updated in the registry with a non null terminator between the two calls to ReqQueryValueEx above. Rather than
//return a potentially non-null terminated block of data, just fail the method call
if (dwReturnedSize >= dwAllocatedSize)
continue;
//NULL terminate the data if it was not returned NULL terminated because it is not stored null terminated in the registry
if (pszPortName[dwReturnedSize/sizeof(TCHAR) - 1] != _T('\0'))
pszPortName[dwReturnedSize/sizeof(TCHAR)] = _T('\0');
}/*local varable*/
//If it looks like "COMX" then
//add it to the array which will be returned
nLen = _tcslen(pszPortName);
if (3 < nLen)
{
if (0 == _tcsnicmp(pszPortName, TEXT("COM"), 3))
{
if(FALSE == isdigit(pszPortName[3]) )
continue;
//Work out the port number
_tcsncpy(pPortName + jj*strMaxLen, pszPortName,
_tcsnlen(pszPortName, strMaxLen));
//_stprintf_s(&portName[jj][0], strMaxLen, TEXT("%s"), pszPortName);
}
else
{
continue;
}/*if 0 == _tcsnicmp(pszPortName, TEXT("COM"), 3)*/
}/*if 3 < nLen*/
LocalFree(pszPortName);
isFound = TRUE;
//Close the key now that we are finished with it
RegCloseKey(hDeviceKey);
}/*INVALID_HANDLE_VALUE != hDeviceKey*/
if(FALSE == isFound)
continue;
//If the port was a serial port, then also try to get its friendly name
{
TCHAR szFriendlyName[1024];
DWORD dwSize;
DWORD dwType;
szFriendlyName[0] = _T('\0');
dwSize = sizeof(szFriendlyName);
dwType = 0;
if( (TRUE == SetupDiGetDeviceRegistryPropertyFunPtr(hDevInfoSet, &devInfo,
SPDRP_DEVICEDESC, &dwType, (PBYTE)(szFriendlyName),
dwSize, &dwSize) ) && (REG_SZ == dwType)
)
{
_tcsncpy(pFriendName + jj*strMaxLen, &szFriendlyName[0],
_tcsnlen(&szFriendlyName[0], strMaxLen));
}
else
{
_stprintf_s(pFriendName + jj*strMaxLen, strMaxLen, TEXT(""));
}/*if SetupDiGetDeviceRegistryPropertyFunPtr */
}/*local variable */
jj++;
}while(1);
HeapFree(GetProcessHeap(), 0, pGuid);
*pNumber = jj;
if(0 <jj)
ret = TRUE;
return ret;
}/*EnumerateComPortSetupAPISetupDiClassGuidsFromNamePort*/
EnumerateComPortRegistry
BOOL EnumerateComPortRegistry(UINT *pNumber, TCHAR *pPortName, int strMaxLen)
{
//What will be the return value from this function (assume the worst)
UINT jj;
BOOL ret;
HKEY hSERIALCOMM;
ret = FALSE;
if ( ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
TEXT("HARDWARE\\DEVICEMAP\\SERIALCOMM"), 0, KEY_QUERY_VALUE, &hSERIALCOMM)
)
{
//Get the max value name and max value lengths
DWORD dwMaxValueNameLen;
DWORD dwMaxValueLen;
DWORD dwQueryInfo;
dwQueryInfo = RegQueryInfoKey(hSERIALCOMM, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
&dwMaxValueNameLen, &dwMaxValueLen, NULL, NULL);
if(ERROR_SUCCESS == dwQueryInfo)
{
DWORD dwMaxValueNameSizeInChars, dwMaxValueNameSizeInBytes,
dwMaxValueDataSizeInChars, dwMaxValueDataSizeInBytes;
DWORD *pValueName;
DWORD *pValueData;
dwMaxValueNameSizeInChars = dwMaxValueNameLen + 1; //Include space for the NULL terminator
dwMaxValueNameSizeInBytes = dwMaxValueNameSizeInChars * sizeof(TCHAR);
dwMaxValueDataSizeInChars = dwMaxValueLen/sizeof(TCHAR) + 1; //Include space for the NULL terminator
dwMaxValueDataSizeInBytes = dwMaxValueDataSizeInChars * sizeof(TCHAR);
//Allocate some space for the value name and value data
pValueName = (DWORD *)(GUID*)HeapAlloc(GetProcessHeap(),
HEAP_GENERATE_EXCEPTIONS| HEAP_ZERO_MEMORY, dwMaxValueNameSizeInBytes);
pValueData = (DWORD *)(GUID*)HeapAlloc(GetProcessHeap(),
HEAP_GENERATE_EXCEPTIONS| HEAP_ZERO_MEMORY, dwMaxValueDataSizeInBytes);
if(NULL != pValueName && NULL != pValueData)
{
//Enumerate all the values underneath HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM
DWORD i;
DWORD dwType;
DWORD dwValueNameSize;
DWORD dwDataSize;
LONG nEnum;
dwValueNameSize = dwMaxValueNameSizeInChars;
dwDataSize = dwMaxValueDataSizeInBytes;
i = 0;
nEnum = RegEnumValue(hSERIALCOMM, i,
(LPSTR)pValueName, &dwValueNameSize, NULL, &dwType,
(LPBYTE)pValueData, &dwDataSize);
jj = 0;
while (ERROR_SUCCESS == nEnum)
{
//If the value is of the correct type, then add it to the array
if (REG_SZ == dwType)
{
_stprintf_s(pPortName + jj*strMaxLen,
strMaxLen, TEXT("%s"), pValueData);
jj++;
}/*if */
//Prepare for the next time around
dwValueNameSize = dwMaxValueNameSizeInChars;
dwDataSize = dwMaxValueDataSizeInBytes;
ZeroMemory(pValueName, dwMaxValueNameSizeInBytes);
ZeroMemory(pValueData, dwMaxValueDataSizeInBytes);
i++;
nEnum = RegEnumValue(hSERIALCOMM, i, (LPSTR)pValueName,
&dwValueNameSize, NULL, &dwType, (LPBYTE)pValueData, &dwDataSize);
}/*while*/
}
else
{
return FALSE;
}/*if NULL != pValueName && NULL != pValueData*/
HeapFree(GetProcessHeap(), 0, pValueName);
HeapFree(GetProcessHeap(), 0, pValueData);
}/*ERROR_SUCCESS == dwQueryInfo*/
//Close the registry key now that we are finished with it
RegCloseKey(hSERIALCOMM);
if (dwQueryInfo != ERROR_SUCCESS)
return FALSE;
}/*ERROR_SUCCESS == RegOpenKeyEx*/
*pNumber = jj;
if(0 <jj)
ret = TRUE;
return ret;
}/*EnumerateComPortRegistry*/
测试
#pragma comment(lib, "winmm.lib")
#define TIMER_BEGIN(TIMER_LABEL) \
{ unsigned int tBegin##TIMER_LABEL, tEnd##TIMER_LABEL; tBegin##TIMER_LABEL = GetTime();
#define TIMER_END(TIMER_LABEL) \
tEnd##TIMER_LABEL = GetTime();\
fprintf(stderr, "%s cost time = %d ms\n", #TIMER_LABEL, tEnd##TIMER_LABEL - tBegin##TIMER_LABEL); \
}
unsigned int GetTime(void)
{
/*winmm.lib*/
return ( unsigned int)timeGetTime();
}/*GetTime*/
int main(int argc, TCHAR argv[])
{
TCHAR portName[MAX_PORT_NUM][MAX_STR_LEN];
TCHAR friendlyName[MAX_PORT_NUM][MAX_STR_LEN];
UINT i;
UINT n;
char* nativeLocale;
nativeLocale = _strdup( setlocale(LC_CTYPE,NULL) );
for(i = 0; i< MAX_PORT_NUM; i++)
ZeroMemory(&portName[i][0], MAX_STR_LEN);
_tprintf(TEXT("\nCreateFile method : \n"));
TIMER_BEGIN(EnumerateComPortByCreateFile);
EnumerateComPortByCreateFile(&n, &portName[0][0], MAX_STR_LEN);
TIMER_END(EnumerateComPortByCreateFile);
_tprintf(TEXT("sought %d:\n"), n);
for(i = 0; i< n; i++)
_tprintf(TEXT("\t%s\n"), &portName[i][0]);
for(i = 0; i< MAX_PORT_NUM; i++)
ZeroMemory(&portName[0][0], MAX_STR_LEN);
_tprintf(TEXT("\nQueryDosDevice method : "));
TIMER_BEGIN(EnumerateComPortQueryDosDevice);
EnumerateComPortQueryDosDevice(&n, &portName[0][0], MAX_STR_LEN);
TIMER_END(EnumerateComPortQueryDosDevice);
_tprintf(TEXT("sought %d:\n"), n);
for(i = 0; i< n; i++)
_tprintf("\t%s\n", &portName[i][0]);
for(i = 0; i< MAX_PORT_NUM; i++)
ZeroMemory(&portName[i][0], MAX_STR_LEN);
_tprintf(TEXT("\nGetDefaultCommConfig method : \n"));
TIMER_BEGIN(EnumerateComPortByGetDefaultCommConfig);
EnumerateComPortByGetDefaultCommConfig(&n, &portName[0][0], MAX_STR_LEN);
TIMER_END(EnumerateComPortByGetDefaultCommConfig);
_tprintf(TEXT("sought %d:\n"), n);
for(i = 0; i< n; i++)
_tprintf(TEXT("\t%s\n"), &portName[i][0]);
for(i = 0; i< MAX_PORT_NUM; i++){
ZeroMemory(&portName[i][0], MAX_STR_LEN);
ZeroMemory(&friendlyName[i][0], MAX_STR_LEN);
}/*for i[i MAX_PORT_NUM]*/
_tprintf(TEXT("\nSetupAPI GUID_DEVINTERFACE_COMPORT method : \n"));
TIMER_BEGIN(EnumerateComPortSetupAPI_GUID_DEVINTERFACE_COMPORT);
EnumerateComPortSetupAPI_GUID_DEVINTERFACE_COMPORT(&n, &portName[0][0],
MAX_STR_LEN, &friendlyName[0][0]);
TIMER_END(EnumerateComPortSetupAPI_GUID_DEVINTERFACE_COMPORT);
_tprintf(TEXT("sought %d:\n"), n);
setlocale(LC_CTYPE, "" );
for(i = 0; i< n; i++)
_tprintf(TEXT("\t%s <%s> \n"), &portName[i][0], &friendlyName[i][0]);
setlocale(LC_CTYPE, nativeLocale);
for(i = 0; i< MAX_PORT_NUM; i++){
ZeroMemory(&portName[i][0], MAX_STR_LEN);
ZeroMemory(&friendlyName[i][0], MAX_STR_LEN);
}/*for i[i MAX_PORT_NUM]*/
_tprintf(TEXT("\nSetupAPI SetupDiClassGuidsFromNamePort method : \n"));
TIMER_BEGIN(EnumerateComPortSetupAPISetupDiClassGuidsFromNamePort);
EnumerateComPortSetupAPISetupDiClassGuidsFromNamePort(&n, &portName[0][0],
MAX_STR_LEN, &friendlyName[0][0]);
TIMER_END(EnumerateComPortSetupAPISetupDiClassGuidsFromNamePort);
_tprintf(TEXT("sought %d:\n"), n);
setlocale(LC_CTYPE, "" );
for(i = 0; i< n; i++)
_tprintf(TEXT("\t%s <%s> \n"), &portName[i][0], &friendlyName[i][0]);
setlocale(LC_CTYPE, nativeLocale);
for(i = 0; i< MAX_PORT_NUM; i++)
ZeroMemory(&portName[i][0], MAX_STR_LEN);
_tprintf(TEXT("\nRegistry method : \n"));
TIMER_BEGIN(EnumerateComPortRegistry);
EnumerateComPortRegistry(&n, &portName[0][0], MAX_STR_LEN);
TIMER_END(EnumerateComPortRegistry);
_tprintf(TEXT("sought %d:\n"), n);
for(i = 0; i< n; i++)
_tprintf(TEXT("\t%s\n"), &portName[i][0]);
return 0;
}/*main*/
结果
CreateFile method :
EnumerateComPortByCreateFile cost time = 81 ms
sought 12:
\\.\COM7
\\.\COM8
\\.\COM9
\\.\COM10
\\.\COM11
\\.\COM12
\\.\COM18
\\.\COM19
\\.\COM20
\\.\COM26
\\.\COM30
\\.\COM31
QueryDosDevice method : EnumerateComPortQueryDosDevice cost time = 2 ms
sought 12:
COM12
COM20
COM30
COM31
COM18
COM19
COM26
COM7
COM8
COM9
COM10
COM11
GetDefaultCommConfig method :
EnumerateComPortByGetDefaultCommConfig cost time = 138 ms
sought 12:
COM7
COM8
COM9
COM10
COM11
COM12
COM18
COM19
COM20
COM26
COM30
COM31
SetupAPI GUID_DEVINTERFACE_COMPORT method :
EnumerateComPortSetupAPI_GUID_DEVINTERFACE_COMPORT cost time = 20 ms
sought 12:
COM26 <XR21V1410 USB UART>
COM7 <USB-SERIAL CH340>
COM8 <USB-SERIAL CH340>
COM9 <USB-SERIAL CH340>
COM10 <USB-SERIAL CH340>
COM11 <USB-SERIAL CH340>
COM12 <USB-SERIAL CH340>
COM19 <USB-SERIAL CH340>
COM18 <USB-SERIAL CH340>
COM20 <USB-SERIAL CH340>
COM30 <Virtual Serial Port 8 (Eltima Software)>
COM31 <Virtual Serial Port 8 (Eltima Software)>
SetupAPI SetupDiClassGuidsFromNamePort method :
EnumerateComPortSetupAPISetupDiClassGuidsFromNamePort cost time = 25 ms
sought 10:
COM11 <USB-SERIAL CH340>
COM12 <USB-SERIAL CH340>
COM26 <XR21V1410 USB UART>
COM19 <USB-SERIAL CH340>
COM18 <USB-SERIAL CH340>
COM20 <USB-SERIAL CH340>
COM7 <USB-SERIAL CH340>
COM8 <USB-SERIAL CH340>
COM9 <USB-SERIAL CH340>
COM10 <USB-SERIAL CH340>
Registry method :
EnumerateComPortRegistry cost time = 0 ms
sought 12:
COM30
COM31
COM7
COM8
COM9
COM10
COM11
COM12
COM19
COM18
COM20
COM26
请按任意键继续. . .
扫描方法对比
说明: 偷下懒,转载 micemik 的博客
作者: micemik
博客: https://www.cnblogs.com/micemik/p/8259330.html
枚举串口方法 | 说明 |
---|---|
EnumPorts | 能够获得历史上曾经在系统中存在过的所有串口,不实用 |
WMI | 无法获取纯粹用软件虚拟出来的串口,例如VSPD串口 |
注册表 | 简便、实用、快速,无遗漏,无多余 |
SetupAPI | GUID_DEVINTERFACE_COMPORT 结果同注册表法,但结果信息太详细,能够分辨出串口类型是纯软件虚拟的,还是蓝牙虚拟的,还是USB虚拟的,等等。 |
SetupAPI | Ports Device information set 结果同注册表法,但结果信息太详细,能够分辨出串口类型是纯软件虚拟的,还是蓝牙虚拟的,还是USB虚拟的,等等。 |
GetDefaultCommConfig | 结果有遗漏 |
QueryDosDevice | 结果同注册表法, |
文件读写法 | 结果同注册表法,但要尝遍255个串口,效率太低。 |