According to this stack overflow thread, PJ Naughter has implemented 9 methods to emunerate com port in Windows. That code was namedEnumSerialPorts.





     As my using, I found some method would list non-existed com ports. for example:



My computer com port be :

But the output of  PJ Naughte's EnumSerialPorts be :

CreateFile method reports
COM1
COM4
COM5
COM98
COM99
QueryDosDevice method reports
COM5
COM1
COM98
COM99
COM4
GetDefaultCommConfig method reports
COM1
COM4
COM5
COM98
COM99
Device Manager (SetupAPI - GUID_DEVINTERFACE_COMPORT) reports
COM1 <通訊連接埠>
COM4 <USB Serial Port>
Device Manager (SetupAPI - Ports Device information set) reports
COM99 <Bluetooth Serial Port>
COM4 <USB Serial Port>
COM1 <通訊連接埠>
COM98 <Bluetooth Serial Port>
COM5 <USB-SERIAL CH340>
EnumPorts method reports
COM1
COM2
COM4
COM7
COM8
COM10
COM6
COM5
COM98
COM99
COM9
COM11
COM12
COM13
COM14
COM16
COM17
COM18
COM19
COM20
COM21
COM22
COM23
COM24
COM25
COM26
COM27
COM28
COM29
COM30
COM31
COM32
COM3
WMI method reports
COM1 <通訊連接埠 (COM1)>
ComDB method reports
COM1
COM3
COM4
COM5
COM6
COM7
COM8
COM9
COM22
COM23
COM24
COM25
COM26
COM27
COM28
COM29
COM30
COM31
COM32
COM98
COM99
Registry method reports
COM98
COM99
COM5
COM1
COM4

Otherwise, that code are too extrvagen, those is hard to separate into an individual function: it is too much dependency on the other functions.



I has reorganized those functions being independent forms in C(instead of C++), those are more portable and useful. I also eliminated some methods which would report unused com ports.



My code be :

/*
Enumerating com ports in Windows original by PJ Naughter, 1998 - 2013
http://www.naughter.com/enumser.html (Web: www.naughter.com, Email: pjna@naughter.com) reorganize by Gaiger Chen , Jul, 2015 NO COPYRIGHT, welcome to use for everyone.
*/ #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)) /*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*/ 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*/ 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*/ 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*/ 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*/ 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 = (GUID*)HeapAlloc(GetProcessHeap(),
HEAP_GENERATE_EXCEPTIONS| HEAP_ZERO_MEMORY, dwMaxValueNameSizeInBytes);
pValueData = (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,
pValueName, &dwValueNameSize, NULL, &dwType,
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, pValueName,
&dwValueNameSize, NULL, &dwType, 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*/ #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*/

The output be :

CreateFile method :
EnumerateComPortByCreateFile cost time = 35 ms
sought 5:
\\.\COM1
\\.\COM4
\\.\COM5
\\.\COM98
\\.\COM99 QueryDosDevice method : EnumerateComPortQueryDosDevice cost time = 7 ms
sought 5:
COM5
COM1
COM98
COM99
COM4 GetDefaultCommConfig method :
EnumerateComPortByGetDefaultCommConfig cost time = 772 ms
sought 5:
COM1
COM4
COM5
COM98
COM99 SetupAPI GUID_DEVINTERFACE_COMPORT method :
EnumerateComPortSetupAPI_GUID_DEVINTERFACE_COMPORT cost time = 12 ms
sought 2:
COM1 <通訊連接埠>
COM4 <USB Serial Port> SetupAPI SetupDiClassGuidsFromNamePort method :
EnumerateComPortSetupAPISetupDiClassGuidsFromNamePort cost time = 10 ms
sought 5:
COM99 <Bluetooth Serial Port>
COM4 <USB Serial Port>
COM1 <通訊連接埠>
COM98 <Bluetooth Serial Port>
COM5 <USB-SERIAL CH340> Registry method :
EnumerateComPortRegistry cost time = 0 ms
sought 5:
COM98
COM99
COM5
COM1
COM4

Note that: EnumerateComPortSetupAPI_GUID_DEVINTERFACE_COMPORT

would not list CH340 port, which is on an Arduino UNO board. (But the same function could list other CH340 port, if it is not on this Arduino). Besides,  this function could not list Bluetooth virtual com ports.

     

     If you would like to use my code, I suggest you use function

BOOL EnumerateComPortSetupAPISetupDiClassGuidsFromNamePort(UINT *pNumber,
TCHAR *pPortName, int strMaxLen, TCHAR *pFriendName)

That is the best one which balances speed and detail; but this one are most complicated in implementation. Otherwise, if you want to implement a function which could list com ports as simple as possible, I recommend use theCreateFilemethod:
that is rudimentary, but sufficient.

Methods Collection of Enumerating Com Port in Windows, by C的更多相关文章

  1. Seeking USB Serial Com Port in Windows Automatically : via PID VID

    After you read previous article, you might know how to operate a com port in Windows.    But that ex ...

  2. 将cocos2dx+lua创建的游戏port到windows phone

    在整个Port的过程中遇到的问题总结例如以下 1.一定要使用最新版本号的cocos2dx,原因大家看一下changelog就知道了,近期的cocos2dx版本号都是在修windows phone上的b ...

  3. kill 8080 port on windows

    1. 查找PID netstat -ano | findstr :yourPortNumber 2. kill进程 taskkill /PID typeyourPIDhere /F

  4. windows防火墙添加规则

    #include <windows.h> #include <crtdbg.h> #include <netfw.h> #include <objbase.h ...

  5. Serial Port Programming using Win32 API(转载)

    In this tutorial we will learn How to communicate with an external device like a microcontroller boa ...

  6. Native code on Windows Phone 8(转)

    Windows Phone 8 introduces the ability to use native code to implement Windows Phone. In this sectio ...

  7. windows下的mysql客户端mysqlworkbench链接虚拟机上CentOS的mysql服务器

    本人在虚拟机上CentOS的Linux环境下安装了mysql服务器,在本地Windows下安装了mysql的客户端mysqlworkbench ,所以就想让windows下的mysql客户端mysql ...

  8. windows环境安装weblogic服务【转】【补】

    我的环境: windows: win10 professional edition jdk: C:\Program Files\Java\jdk1.6.0_45 weblogic安装目录 (WEBLO ...

  9. mongodb在windows下的安装

    Windows下安装MongoDB 1.下载MongoDB数据库http://fastdl.mongodb.org/win32/mongodb-win32-i386-1.6.5.zip: 2.将安装文 ...

随机推荐

  1. QlikSense系列(4)——QlikSense管理

    QlikSense管理主要通过QMC界面,在安装成功后,首先需要导入用户,QlikSense本身不能创建和验证用户,只能借助第三方系统, 笔者只使用过Windows账户和AD域用户: 1.Window ...

  2. C#调用SMS短信接口,轻松搞定发送短信的任务。。。。

    首先我们需要去这里http://sms.webchinese.cn/申请一个账号和短信接口秘钥,在该网址下有许多语言的demo介绍,下面我主要为大家贴一个C#中的Helper类: using Syst ...

  3. 10) 十分钟学会android--app数据保存三种方式

    虽然可以在onPause()时保存一些信息以免用户的使用进度被丢失,但大多数Android app仍然是需执行保存数据的动作.大多数较好的apps都需要保存用户的设置信息,而且有一些apps必须维护大 ...

  4. 开发手机APP过程,不同使用场景APP搜索框的样式及区别

    搜索框是 app 内最常见的控件之一,可以帮助用户快速又精准找到期望的内容与功能.不同的使用场景下,根据页面中搜索的重要程度,搜索框也有着不同的样式. 下面就常州开发APP公司和大家聊聊常见的四种样式 ...

  5. 当接口上配了 FeignClient 和 RequestMapping 两个注解,结果错误提示 重复mapping处理方法

    再接手老文档的时候,发现有这么一个问题 错误显示为: 原文档写法: 解决方法: 这是一个编译时写法的问题,将上方的RequestMapping去掉,然后把路径放在下面的PostMapping 便可以正 ...

  6. 三.Windows I/O模型之事件选择(WSAEventSelect )模型

    1.事件选择模型:和异步选择模型类似的是,它也允许应用程序在一个或多个套接字上,接收以事件为基础的网络事件通知.对于异步选择模型采用的网络事件来说,它们均可原封不动地移植到事件选择模型.事件选择模型和 ...

  7. LINUX 环境安装 jdk-tomcat安装

    linux版本两种安装方式 卸载自带jdk $rpm -qa | grep java $ rpm -e --nodeps java-**-openjdk-*$ rpm -e --nodeps java ...

  8. 图片无损放大工具PhotoZoom如何进行打印设置

    我们使用PhotoZoom对照片进行无失真放大后,想将照片给打印出来需要设置一些常规参数时.那么这些参数我们该从哪里设置,怎么设置呢? PhotoZoom下载:pan.baidu.com/s/1cXb ...

  9. 基于Nginx服务的用户认证

    通过Nginx实现web页面的用户认证,用户名为:admin,密码为:654321 1.修改Nginx配置文件 # vim /usr/local/nginx/conf/nginx.conf ..... ...

  10. java中的标识符

    程序员自己定义的名称,例如类名,方法名,变量等等 标识符命名的规则 1.只能由字母(a-z,A-Z),数字(0-9),下划线(_)和美元符号($)组成 2.不能以数字开头 3.不能与关键字重名 4.严 ...