扫描windows串行设备的方法

前言

接到一个临时任务,写一个串行口小工具连续读写某设备,而设备还未准备好。于是,安装了一下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个串口,效率太低。

猜你喜欢

转载自blog.csdn.net/hylaking/article/details/89496100
今日推荐