• Methods Collection of Enumerating Com Port in Windows, by C



         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
    ", 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
    ", dwError));
       return -1;     
      }/*if */
    
    
      //printf("dwChars = %d
    ", dwChars);
      i = 0;
      jj = 0;
      while (TEXT('') != pDevices[i] )
      {
       TCHAR* pszCurrentDevice;
       size_t nLen;
       pszCurrentDevice = &(pDevices[i]);
       nLen = _tcslen(pszCurrentDevice);
    
       //_tprintf(TEXT("%s
    "), &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(''); 
    
     //Get the Windows System32 directory
     
     
     if(0 == GetSystemDirectory(szFullPath, _countof(szFullPath)))
     {
      _tprintf(TEXT("CEnumerateSerial::UsingSetupAPI1 failed, Error:%u
    "), 
       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('');
        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(''))
         pszPortName[dwReturnedSize/sizeof(TCHAR)] = _T('');
       }/*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('');
       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(''); 
    
     //Get the Windows System32 directory
     
     
     if(0 == GetSystemDirectory(szFullPath, _countof(szFullPath)))
     {
      _tprintf(TEXT("CEnumerateSerial::UsingSetupAPI1 failed, Error:%u
    "), 
       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('');
        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(''))
         pszPortName[dwReturnedSize/sizeof(TCHAR)] = _T('');
       }/*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('');
       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_MACHINEHARDWAREDEVICEMAPSERIALCOMM
        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
    ", 
       #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("
    CreateFile method : 
    "));
    TIMER_BEGIN(EnumerateComPortByCreateFile);
     EnumerateComPortByCreateFile(&n, &portName[0][0], MAX_STR_LEN);
    TIMER_END(EnumerateComPortByCreateFile);
     _tprintf(TEXT("sought %d:
    "), n);
     for(i = 0; i< n; i++) 
      _tprintf(TEXT("	%s
    "), &portName[i][0]);
     
    
    
    
     for(i = 0; i< MAX_PORT_NUM; i++)
      ZeroMemory(&portName[0][0], MAX_STR_LEN);
    
     _tprintf(TEXT("
    QueryDosDevice method : ")); 
    TIMER_BEGIN(EnumerateComPortQueryDosDevice); 
     EnumerateComPortQueryDosDevice(&n, &portName[0][0], MAX_STR_LEN);
    TIMER_END(EnumerateComPortQueryDosDevice);
     _tprintf(TEXT("sought %d:
    "), n);
    
     for(i = 0; i< n; i++)
      _tprintf("	%s
    ", &portName[i][0]);
     
    
    
     
    
     for(i = 0; i< MAX_PORT_NUM; i++)
      ZeroMemory(&portName[i][0], MAX_STR_LEN);
    
     _tprintf(TEXT("
    GetDefaultCommConfig method : 
    "));
    TIMER_BEGIN(EnumerateComPortByGetDefaultCommConfig);
     EnumerateComPortByGetDefaultCommConfig(&n, &portName[0][0], MAX_STR_LEN);
    TIMER_END(EnumerateComPortByGetDefaultCommConfig);
     _tprintf(TEXT("sought %d:
    "), n);
    
     for(i = 0; i< n; i++) 
      _tprintf(TEXT("	%s
    "), &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("
    SetupAPI GUID_DEVINTERFACE_COMPORT method : 
    ")); 
    
    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);
     
     setlocale(LC_CTYPE, "" );   
     for(i = 0; i< n; i++) 
      _tprintf(TEXT("	%s <%s> 
    "), &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("
    SetupAPI SetupDiClassGuidsFromNamePort method : 
    "));
     
    TIMER_BEGIN(EnumerateComPortSetupAPISetupDiClassGuidsFromNamePort);
     EnumerateComPortSetupAPISetupDiClassGuidsFromNamePort(&n, &portName[0][0], 
      MAX_STR_LEN, &friendlyName[0][0]);
    TIMER_END(EnumerateComPortSetupAPISetupDiClassGuidsFromNamePort);
    
     _tprintf(TEXT("sought %d:
    "), n);
     
     setlocale(LC_CTYPE, "" );   
     for(i = 0; i< n; i++) 
      _tprintf(TEXT("	%s <%s> 
    "), &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("
    Registry method : 
    "));
    
    TIMER_BEGIN(EnumerateComPortRegistry);
     EnumerateComPortRegistry(&n, &portName[0][0], MAX_STR_LEN);
    TIMER_END(EnumerateComPortRegistry);
     _tprintf(TEXT("sought %d:
    "), n);
     for(i = 0; i< n; i++) 
      _tprintf(TEXT("	%s
    "), &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.

  • 相关阅读:
    nodejs sequelize 对应数据库操作符的定义
    nodejs利用sequelize-auto 根据数据库的table 生成model
    微信小程序: rpx与px,rem相互转换
    vue 父组件通过props向子组件传递数据/方法的方式
    小程序-wepy学习
    [考试反思]1026csp-s模拟测试88:发展
    [考试反思]1025csp-s模拟测试87:生存
    [考试反思]1024csp-s模拟测试86:消耗
    [考试反思]1024csp-s模拟测试85:以为
    [考试反思]1023csp-s模拟测试84:精妙
  • 原文地址:https://www.cnblogs.com/wzzkaifa/p/7000216.html
Copyright © 2020-2023  润新知