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 :

  1. CreateFile method reports
  2. COM1
  3. COM4
  4. COM5
  5. COM98
  6. COM99
  7. QueryDosDevice method reports
  8. COM5
  9. COM1
  10. COM98
  11. COM99
  12. COM4
  13. GetDefaultCommConfig method reports
  14. COM1
  15. COM4
  16. COM5
  17. COM98
  18. COM99
  19. Device Manager (SetupAPI - GUID_DEVINTERFACE_COMPORT) reports
  20. COM1 <通訊連接埠>
  21. COM4 <USB Serial Port>
  22. Device Manager (SetupAPI - Ports Device information set) reports
  23. COM99 <Bluetooth Serial Port>
  24. COM4 <USB Serial Port>
  25. COM1 <通訊連接埠>
  26. COM98 <Bluetooth Serial Port>
  27. COM5 <USB-SERIAL CH340>
  28. EnumPorts method reports
  29. COM1
  30. COM2
  31. COM4
  32. COM7
  33. COM8
  34. COM10
  35. COM6
  36. COM5
  37. COM98
  38. COM99
  39. COM9
  40. COM11
  41. COM12
  42. COM13
  43. COM14
  44. COM16
  45. COM17
  46. COM18
  47. COM19
  48. COM20
  49. COM21
  50. COM22
  51. COM23
  52. COM24
  53. COM25
  54. COM26
  55. COM27
  56. COM28
  57. COM29
  58. COM30
  59. COM31
  60. COM32
  61. COM3
  62. WMI method reports
  63. COM1 <通訊連接埠 (COM1)>
  64. ComDB method reports
  65. COM1
  66. COM3
  67. COM4
  68. COM5
  69. COM6
  70. COM7
  71. COM8
  72. COM9
  73. COM22
  74. COM23
  75. COM24
  76. COM25
  77. COM26
  78. COM27
  79. COM28
  80. COM29
  81. COM30
  82. COM31
  83. COM32
  84. COM98
  85. COM99
  86. Registry method reports
  87. COM98
  88. COM99
  89. COM5
  90. COM1
  91. 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 :

  1. /*
  2. Enumerating com ports in Windows
  3.  
  4. original by PJ Naughter, 1998 - 2013
  5. http://www.naughter.com/enumser.html (Web: www.naughter.com, Email: pjna@naughter.com)
  6.  
  7. reorganize by Gaiger Chen , Jul, 2015
  8.  
  9. NO COPYRIGHT, welcome to use for everyone.
  10. */
  11.  
  12. #include <windows.h>
  13.  
  14. #include <stdio.h>
  15. #include <tchar.h>
  16. #include <setupapi.h>
  17. #include <locale.h>
  18.  
  19. #define MAX_PORT_NUM (256)
  20. #define MAX_STR_LEN (256*sizeof(TCHAR))
  21.  
  22. /*assure portName be double ARRAY , not double point*/
  23. BOOL EnumerateComPortByCreateFile(UINT *pNumber, TCHAR *pPortName, int strMaxLen)
  24. {
  25. UINT i, jj;
  26. INT ret;
  27. TCHAR *pTempPortName;
  28.  
  29. *pNumber = 0;
  30. jj = 0;
  31. pTempPortName = (TCHAR*)HeapAlloc(GetProcessHeap(),
  32. HEAP_GENERATE_EXCEPTIONS|HEAP_ZERO_MEMORY,
  33. strMaxLen*sizeof(pTempPortName));
  34.  
  35. ret = FALSE;
  36.  
  37. for (i = 1; i<= 255; i++){
  38. HANDLE hSerial;
  39.  
  40. _stprintf_s(pTempPortName, strMaxLen, TEXT("\\\\.\\COM%u"), i);
  41.  
  42. hSerial = CreateFile(pTempPortName, GENERIC_READ | GENERIC_WRITE,
  43. 0, 0, OPEN_EXISTING, 0, 0);
  44.  
  45. if(INVALID_HANDLE_VALUE == hSerial)
  46. continue;
  47.  
  48. _tcsncpy(pPortName + jj*strMaxLen, pTempPortName,
  49. _tcsnlen(pTempPortName, strMaxLen));
  50.  
  51. jj++;
  52. }/*for [i MAX_PORT_NUM] */
  53.  
  54. HeapFree(GetProcessHeap(), 0, pTempPortName); pTempPortName = NULL;
  55. *pNumber = jj;
  56.  
  57. if(0 <jj)
  58. ret = TRUE;
  59.  
  60. return ret;
  61. }/*EnumerateComPortByCreateFile*/
  62.  
  63. BOOL EnumerateComPortQueryDosDevice(UINT *pNumber, TCHAR *pPortName, int strMaxLen)
  64. {
  65. UINT i, jj;
  66. INT ret;
  67.  
  68. OSVERSIONINFOEX osvi;
  69. ULONGLONG dwlConditionMask;
  70. DWORD dwChars;
  71.  
  72. TCHAR *pDevices;
  73. UINT nChars;
  74.  
  75. ret = FALSE;
  76.  
  77. memset(&osvi, 0, sizeof(osvi));
  78. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  79. osvi.dwPlatformId = VER_PLATFORM_WIN32_NT;
  80. dwlConditionMask = 0;
  81.  
  82. VER_SET_CONDITION(dwlConditionMask, VER_PLATFORMID, VER_EQUAL);
  83.  
  84. if(FALSE == VerifyVersionInfo(&osvi, VER_PLATFORMID, dwlConditionMask))
  85. {
  86. DWORD dwError = GetLastError();
  87. _tprintf(TEXT("VerifyVersionInfo error, %d\n", dwError));
  88. return -1;
  89. }/*if*/
  90.  
  91. pDevices = NULL;
  92.  
  93. nChars = 4096;
  94. pDevices = (TCHAR*)HeapAlloc(GetProcessHeap(),
  95. HEAP_GENERATE_EXCEPTIONS, nChars*sizeof(TCHAR));
  96.  
  97. while(0 < nChars)
  98. {
  99. dwChars = QueryDosDevice(NULL, pDevices, nChars);
  100.  
  101. if(0 == dwChars)
  102. {
  103. DWORD dwError = GetLastError();
  104.  
  105. if(ERROR_INSUFFICIENT_BUFFER == dwError)
  106. {
  107. nChars *= 2;
  108. HeapFree(GetProcessHeap(), 0, pDevices);
  109. pDevices = (TCHAR*)HeapAlloc(GetProcessHeap(),
  110. HEAP_GENERATE_EXCEPTIONS, nChars*sizeof(TCHAR));
  111.  
  112. continue;
  113. }/*if ERROR_INSUFFICIENT_BUFFER == dwError*/
  114.  
  115. _tprintf(TEXT("QueryDosDevice error, %d\n", dwError));
  116. return -1;
  117. }/*if */
  118.  
  119. //printf("dwChars = %d\n", dwChars);
  120. i = 0;
  121. jj = 0;
  122. while (TEXT('\0') != pDevices[i] )
  123. {
  124. TCHAR* pszCurrentDevice;
  125. size_t nLen;
  126. pszCurrentDevice = &(pDevices[i]);
  127. nLen = _tcslen(pszCurrentDevice);
  128.  
  129. //_tprintf(TEXT("%s\n"), &pTargetPathStr[i]);
  130. if (3 < nLen)
  131. {
  132. if ((0 == _tcsnicmp(pszCurrentDevice, TEXT("COM"), 3))
  133. && FALSE != isdigit(pszCurrentDevice[3]) )
  134. {
  135. //Work out the port number
  136. _tcsncpy(pPortName + jj*strMaxLen,
  137. pszCurrentDevice, MAX_STR_LEN);
  138. jj++;
  139.  
  140. }
  141. }
  142.  
  143. i += (nLen + 1);
  144. }
  145.  
  146. break;
  147. }/*while*/
  148.  
  149. if(NULL != pDevices)
  150. HeapFree(GetProcessHeap(), 0, pDevices);
  151.  
  152. *pNumber = jj;
  153.  
  154. if(0 < jj)
  155. ret = TRUE;
  156.  
  157. return ret;
  158. }/*EnumerateComPortByQueryDosDevice*/
  159.  
  160. BOOL EnumerateComPortByGetDefaultCommConfig(UINT *pNumber, TCHAR *pPortName,
  161. int strMaxLen)
  162. {
  163. UINT i, jj;
  164. INT ret;
  165.  
  166. TCHAR *pTempPortName;
  167.  
  168. pTempPortName = (TCHAR*)HeapAlloc(GetProcessHeap(),
  169. HEAP_GENERATE_EXCEPTIONS| HEAP_ZERO_MEMORY, strMaxLen);
  170.  
  171. *pNumber = 0;
  172. jj = 0;
  173. ret = FALSE;
  174.  
  175. for (i = 1; i<=255; i++){
  176.  
  177. //Form the Raw device name
  178. COMMCONFIG cc;
  179. DWORD dwSize ;
  180.  
  181. dwSize = sizeof(COMMCONFIG);
  182.  
  183. _stprintf_s(pTempPortName, strMaxLen/2, TEXT("COM%u"), i);
  184.  
  185. if (FALSE == GetDefaultCommConfig(pTempPortName, &cc, &dwSize))
  186. continue;
  187.  
  188. _tcsncpy(pPortName + jj*strMaxLen, pTempPortName,
  189. _tcsnlen(pTempPortName, strMaxLen));
  190. jj++;
  191. }/*for [1 255] */
  192.  
  193. HeapFree(GetProcessHeap(), 0, pTempPortName);
  194. pTempPortName = NULL;
  195.  
  196. *pNumber = jj;
  197.  
  198. if(0 <jj)
  199. ret = TRUE;
  200.  
  201. return ret;
  202. }/*EnumerateComPortByGetDefaultCommConfig*/
  203.  
  204. BOOL EnumerateComPortSetupAPI_GUID_DEVINTERFACE_COMPORT(UINT *pNumber,
  205. TCHAR *pPortName, int strMaxLen, TCHAR *pFriendName)
  206. {
  207. UINT i, jj;
  208. INT ret;
  209.  
  210. TCHAR *pTempPortName;
  211. HMODULE hLibrary;
  212. TCHAR szFullPath[_MAX_PATH];
  213.  
  214. GUID guid;
  215. HDEVINFO hDevInfoSet;
  216.  
  217. typedef HKEY (__stdcall SetupDiOpenDevRegKeyFunType)
  218. (HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM);
  219.  
  220. //typedef BOOL (__stdcall SetupDiClassGuidsFromNameFunType)
  221. // (LPCTSTR, LPGUID, DWORD, PDWORD);
  222.  
  223. typedef BOOL (__stdcall SetupDiDestroyDeviceInfoListFunType)
  224. (HDEVINFO);
  225. typedef BOOL (__stdcall SetupDiEnumDeviceInfoFunType)
  226. (HDEVINFO, DWORD, PSP_DEVINFO_DATA);
  227.  
  228. typedef HDEVINFO (__stdcall SetupDiGetClassDevsFunType)
  229. (LPGUID, LPCTSTR, HWND, DWORD);
  230.  
  231. typedef BOOL (__stdcall SetupDiGetDeviceRegistryPropertyFunType)
  232. (HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD);
  233.  
  234. SetupDiOpenDevRegKeyFunType* SetupDiOpenDevRegKeyFunPtr;
  235.  
  236. SetupDiGetClassDevsFunType *SetupDiGetClassDevsFunPtr;
  237. SetupDiGetDeviceRegistryPropertyFunType *SetupDiGetDeviceRegistryPropertyFunPtr;
  238.  
  239. SetupDiDestroyDeviceInfoListFunType *SetupDiDestroyDeviceInfoListFunPtr;
  240. SetupDiEnumDeviceInfoFunType *SetupDiEnumDeviceInfoFunPtr;
  241.  
  242. BOOL bMoreItems;
  243. SP_DEVINFO_DATA devInfo;
  244.  
  245. ret = FALSE;
  246. jj = 0;
  247. szFullPath[0] = _T('\0');
  248.  
  249. //Get the Windows System32 directory
  250.  
  251. if(0 == GetSystemDirectory(szFullPath, _countof(szFullPath)))
  252. {
  253. _tprintf(TEXT("CEnumerateSerial::UsingSetupAPI1 failed, Error:%u\n"),
  254. GetLastError());
  255. return FALSE;
  256. }/*if*/
  257.  
  258. //Setup the full path and delegate to LoadLibrary
  259. #pragma warning(suppress: 6102) //There is a bug with the SAL annotation of GetSystemDirectory in the Windows 8.1 SDK
  260. _tcscat_s(szFullPath, _countof(szFullPath), _T("\\"));
  261. _tcscat_s(szFullPath, _countof(szFullPath), TEXT("SETUPAPI.DLL"));
  262. hLibrary = LoadLibrary(szFullPath);
  263.  
  264. SetupDiOpenDevRegKeyFunPtr =
  265. (SetupDiOpenDevRegKeyFunType*)GetProcAddress(hLibrary, "SetupDiOpenDevRegKey");
  266.  
  267. #if defined _UNICODE
  268. SetupDiGetClassDevsFunPtr =
  269. (SetupDiGetClassDevsFunType*)GetProcAddress(hLibrary, "SetupDiGetClassDevsW");
  270. SetupDiGetDeviceRegistryPropertyFunPtr = (SetupDiGetDeviceRegistryPropertyFunType*)
  271. GetProcAddress(hLibrary, "SetupDiGetDeviceRegistryPropertyW");
  272. #else
  273. SetupDiGetClassDevsFunPtr =
  274. (SetupDiGetClassDevsFunType*)GetProcAddress(hLibrary, "SetupDiGetClassDevsA");
  275.  
  276. SetupDiGetDeviceRegistryPropertyFunPtr = (SetupDiGetDeviceRegistryPropertyFunType*)
  277. GetProcAddress(hLibrary, "SetupDiGetDeviceRegistryPropertyA");
  278. #endif
  279.  
  280. SetupDiDestroyDeviceInfoListFunPtr = (SetupDiDestroyDeviceInfoListFunType*)
  281. GetProcAddress(hLibrary, "SetupDiDestroyDeviceInfoList");
  282.  
  283. SetupDiEnumDeviceInfoFunPtr = (SetupDiEnumDeviceInfoFunType*)
  284. GetProcAddress(hLibrary, "SetupDiEnumDeviceInfo");
  285.  
  286. guid = GUID_DEVINTERFACE_COMPORT;
  287.  
  288. hDevInfoSet = SetupDiGetClassDevsFunPtr(&guid, NULL, NULL,
  289. DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
  290.  
  291. if (INVALID_HANDLE_VALUE == hDevInfoSet)
  292. {
  293. //Set the error to report
  294. _tprintf(TEXT("error lpfnSETUPDIGETCLASSDEVS, %d"), GetLastError());
  295. return FALSE;
  296. }/*if */
  297.  
  298. //bMoreItems = TRUE;
  299. devInfo.cbSize = sizeof(SP_DEVINFO_DATA);
  300. i = 0;
  301. jj = 0;
  302.  
  303. do
  304. {
  305. HKEY hDeviceKey;
  306. BOOL isFound;
  307.  
  308. isFound = FALSE;
  309. bMoreItems = SetupDiEnumDeviceInfoFunPtr(hDevInfoSet, i, &devInfo);
  310.  
  311. if(FALSE == bMoreItems)
  312. break;
  313.  
  314. i++;
  315.  
  316. hDeviceKey = SetupDiOpenDevRegKeyFunPtr(hDevInfoSet, &devInfo,
  317. DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
  318.  
  319. if (INVALID_HANDLE_VALUE != hDeviceKey)
  320. {
  321. int nPort;
  322. size_t nLen;
  323. LPTSTR pszPortName;
  324.  
  325. nPort = 0;
  326. pszPortName = NULL;
  327.  
  328. {
  329. //First query for the size of the registry value
  330. DWORD dwType;
  331. DWORD dwDataSize;
  332. LONG err;
  333. DWORD dwAllocatedSize;
  334. DWORD dwReturnedSize;
  335. dwType = 0; dwDataSize = 0;
  336.  
  337. err = RegQueryValueEx(hDeviceKey, TEXT("PortName"), NULL,
  338. &dwType, NULL, &dwDataSize);
  339.  
  340. if (ERROR_SUCCESS != err)
  341. continue;
  342.  
  343. //Ensure the value is a string
  344. if (dwType != REG_SZ)
  345. continue;
  346.  
  347. //Allocate enough bytes for the return value
  348. dwAllocatedSize = dwDataSize + sizeof(TCHAR);
  349.  
  350. /* +sizeof(TCHAR) is to allow us to NULL terminate
  351. the data if it is not null terminated in the registry
  352. */
  353.  
  354. pszPortName = (LPTSTR)LocalAlloc(LMEM_FIXED, dwAllocatedSize);
  355.  
  356. if (pszPortName == NULL)
  357. continue;
  358.  
  359. //Recall RegQueryValueEx to return the data
  360. pszPortName[0] = _T('\0');
  361. dwReturnedSize = dwAllocatedSize;
  362.  
  363. err = RegQueryValueEx(hDeviceKey, TEXT("PortName"), NULL,
  364. &dwType, (LPBYTE)pszPortName, &dwReturnedSize);
  365.  
  366. if (ERROR_SUCCESS != err)
  367. {
  368. LocalFree(pszPortName);
  369. pszPortName = NULL;
  370. continue;
  371. }
  372.  
  373. //Handle the case where the data just returned is the same size as the allocated size. This could occur where the data
  374. //has been updated in the registry with a non null terminator between the two calls to ReqQueryValueEx above. Rather than
  375. //return a potentially non-null terminated block of data, just fail the method call
  376. if (dwReturnedSize >= dwAllocatedSize)
  377. continue;
  378.  
  379. //NULL terminate the data if it was not returned NULL terminated because it is not stored null terminated in the registry
  380. if (pszPortName[dwReturnedSize/sizeof(TCHAR) - 1] != _T('\0'))
  381. pszPortName[dwReturnedSize/sizeof(TCHAR)] = _T('\0');
  382. }/*local varable*/
  383.  
  384. //If it looks like "COMX" then
  385. //add it to the array which will be returned
  386. nLen = _tcslen(pszPortName);
  387.  
  388. if (3 < nLen)
  389. {
  390. if (0 == _tcsnicmp(pszPortName, TEXT("COM"), 3))
  391. {
  392. if(FALSE == isdigit(pszPortName[3]) )
  393. continue;
  394.  
  395. //Work out the port number
  396. _tcsncpy(pPortName + jj*strMaxLen, pszPortName,
  397. _tcsnlen(pszPortName, strMaxLen));
  398.  
  399. //_stprintf_s(&portName[jj][0], strMaxLen, TEXT("%s"), pszPortName);
  400. } else
  401. {
  402. continue;
  403. }/*if 0 == _tcsnicmp(pszPortName, TEXT("COM"), 3)*/
  404. }/*if 3 < nLen*/
  405.  
  406. LocalFree(pszPortName);
  407. isFound = TRUE;
  408.  
  409. //Close the key now that we are finished with it
  410. RegCloseKey(hDeviceKey);
  411. }/*INVALID_HANDLE_VALUE != hDeviceKey*/
  412.  
  413. if(FALSE == isFound)
  414. continue;
  415.  
  416. //If the port was a serial port, then also try to get its friendly name
  417. {
  418. TCHAR szFriendlyName[1024];
  419. DWORD dwSize;
  420. DWORD dwType;
  421. szFriendlyName[0] = _T('\0');
  422. dwSize = sizeof(szFriendlyName);
  423. dwType = 0;
  424.  
  425. if( (TRUE == SetupDiGetDeviceRegistryPropertyFunPtr(hDevInfoSet, &devInfo,
  426. SPDRP_DEVICEDESC, &dwType, (PBYTE)(szFriendlyName),
  427. dwSize, &dwSize) ) && (REG_SZ == dwType)
  428. )
  429. {
  430. _tcsncpy(pFriendName + jj*strMaxLen, &szFriendlyName[0],
  431. _tcsnlen(&szFriendlyName[0], strMaxLen));
  432. }
  433. else
  434. {
  435. _stprintf_s(pFriendName + jj*strMaxLen, strMaxLen, TEXT(""));
  436. }/*if SetupDiGetDeviceRegistryPropertyFunPtr */
  437. }/*local variable */
  438.  
  439. jj++;
  440. }while(1);
  441.  
  442. *pNumber = jj;
  443. if(0 <jj)
  444. ret = TRUE;
  445.  
  446. return ret;
  447. }/*EnumerateComPortSetupAPI_GUID_DEVINTERFACE_COMPORT*/
  448.  
  449. BOOL EnumerateComPortSetupAPISetupDiClassGuidsFromNamePort(UINT *pNumber,
  450. TCHAR *pPortName, int strMaxLen, TCHAR *pFriendName)
  451. {
  452. UINT i, jj;
  453. INT ret;
  454.  
  455. TCHAR *pTempPortName;
  456. HMODULE hLibrary;
  457. TCHAR szFullPath[_MAX_PATH];
  458.  
  459. GUID *pGuid;
  460. DWORD dwGuids;
  461. HDEVINFO hDevInfoSet;
  462.  
  463. typedef HKEY (__stdcall SetupDiOpenDevRegKeyFunType)
  464. (HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM);
  465.  
  466. typedef BOOL (__stdcall SetupDiClassGuidsFromNameFunType)
  467. (LPCTSTR, LPGUID, DWORD, PDWORD);
  468.  
  469. typedef BOOL (__stdcall SetupDiDestroyDeviceInfoListFunType)
  470. (HDEVINFO);
  471. typedef BOOL (__stdcall SetupDiEnumDeviceInfoFunType)
  472. (HDEVINFO, DWORD, PSP_DEVINFO_DATA);
  473.  
  474. typedef HDEVINFO (__stdcall SetupDiGetClassDevsFunType)
  475. (LPGUID, LPCTSTR, HWND, DWORD);
  476.  
  477. typedef BOOL (__stdcall SetupDiGetDeviceRegistryPropertyFunType)
  478. (HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD);
  479.  
  480. SetupDiOpenDevRegKeyFunType* SetupDiOpenDevRegKeyFunPtr;
  481.  
  482. SetupDiClassGuidsFromNameFunType *SetupDiClassGuidsFromNameFunPtr;
  483. SetupDiGetClassDevsFunType *SetupDiGetClassDevsFunPtr;
  484. SetupDiGetDeviceRegistryPropertyFunType *SetupDiGetDeviceRegistryPropertyFunPtr;
  485.  
  486. SetupDiDestroyDeviceInfoListFunType *SetupDiDestroyDeviceInfoListFunPtr;
  487. SetupDiEnumDeviceInfoFunType *SetupDiEnumDeviceInfoFunPtr;
  488.  
  489. BOOL bMoreItems;
  490. SP_DEVINFO_DATA devInfo;
  491.  
  492. ret = FALSE;
  493. jj = 0;
  494. szFullPath[0] = _T('\0');
  495.  
  496. //Get the Windows System32 directory
  497.  
  498. if(0 == GetSystemDirectory(szFullPath, _countof(szFullPath)))
  499. {
  500. _tprintf(TEXT("CEnumerateSerial::UsingSetupAPI1 failed, Error:%u\n"),
  501. GetLastError());
  502. return FALSE;
  503. }/*if*/
  504.  
  505. //Setup the full path and delegate to LoadLibrary
  506. #pragma warning(suppress: 6102) //There is a bug with the SAL annotation of GetSystemDirectory in the Windows 8.1 SDK
  507. _tcscat_s(szFullPath, _countof(szFullPath), _T("\\"));
  508. _tcscat_s(szFullPath, _countof(szFullPath), TEXT("SETUPAPI.DLL"));
  509. hLibrary = LoadLibrary(szFullPath);
  510.  
  511. SetupDiOpenDevRegKeyFunPtr =
  512. (SetupDiOpenDevRegKeyFunType*)GetProcAddress(hLibrary, "SetupDiOpenDevRegKey");
  513.  
  514. #if defined _UNICODE
  515. SetupDiClassGuidsFromNameFunPtr = (SetupDiClassGuidsFromNameFunType*)
  516. GetProcAddress(hLibrary, "SetupDiGetDeviceRegistryPropertyW");
  517. SetupDiGetClassDevsFunPtr =
  518. (SetupDiGetClassDevsFunType*)GetProcAddress(hLibrary, "SetupDiGetClassDevsW");
  519. SetupDiGetDeviceRegistryPropertyFunPtr
  520. = (SetupDiGetDeviceRegistryPropertyFunType*)GetProcAddress(hLibrary, "SetupDiGetDeviceRegistryPropertyW");
  521. #else
  522. SetupDiClassGuidsFromNameFunPtr = (SetupDiClassGuidsFromNameFunType*)
  523. GetProcAddress(hLibrary, "SetupDiClassGuidsFromNameA");
  524. SetupDiGetClassDevsFunPtr = (SetupDiGetClassDevsFunType*)
  525. GetProcAddress(hLibrary, "SetupDiGetClassDevsA");
  526. SetupDiGetDeviceRegistryPropertyFunPtr = (SetupDiGetDeviceRegistryPropertyFunType*)
  527. GetProcAddress(hLibrary, "SetupDiGetDeviceRegistryPropertyA");
  528. #endif
  529.  
  530. SetupDiDestroyDeviceInfoListFunPtr = (SetupDiDestroyDeviceInfoListFunType*)
  531. GetProcAddress(hLibrary, "SetupDiDestroyDeviceInfoList");
  532.  
  533. SetupDiEnumDeviceInfoFunPtr = (SetupDiEnumDeviceInfoFunType*)
  534. GetProcAddress(hLibrary, "SetupDiEnumDeviceInfo");
  535.  
  536. //First need to convert the name "Ports" to a GUID using SetupDiClassGuidsFromName
  537. dwGuids = 0;
  538. SetupDiClassGuidsFromNameFunPtr(TEXT("Ports"), NULL, 0, &dwGuids);
  539.  
  540. if(0 == dwGuids)
  541. return FALSE;
  542.  
  543. //Allocate the needed memory
  544. pGuid = (GUID*)HeapAlloc(GetProcessHeap(),
  545. HEAP_GENERATE_EXCEPTIONS, dwGuids * sizeof(GUID));
  546.  
  547. if(NULL == pGuid)
  548. return FALSE;
  549.  
  550. //Call the function again
  551.  
  552. if (FALSE == SetupDiClassGuidsFromNameFunPtr(TEXT("Ports"),
  553. pGuid, dwGuids, &dwGuids))
  554. {
  555. return FALSE;
  556. }/*if*/
  557.  
  558. hDevInfoSet = SetupDiGetClassDevsFunPtr(pGuid, NULL, NULL,
  559. DIGCF_PRESENT /*| DIGCF_DEVICEINTERFACE*/);
  560.  
  561. if (INVALID_HANDLE_VALUE == hDevInfoSet)
  562. {
  563. //Set the error to report
  564. _tprintf(TEXT("error SetupDiGetClassDevsFunPtr, %d"), GetLastError());
  565. return FALSE;
  566. }/*if */
  567.  
  568. //bMoreItems = TRUE;
  569. devInfo.cbSize = sizeof(SP_DEVINFO_DATA);
  570. i = 0;
  571. jj = 0;
  572.  
  573. do
  574. {
  575. HKEY hDeviceKey;
  576. BOOL isFound;
  577.  
  578. isFound = FALSE;
  579. bMoreItems = SetupDiEnumDeviceInfoFunPtr(hDevInfoSet, i, &devInfo);
  580. if(FALSE == bMoreItems)
  581. break;
  582.  
  583. i++;
  584.  
  585. hDeviceKey = SetupDiOpenDevRegKeyFunPtr(hDevInfoSet, &devInfo,
  586. DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
  587.  
  588. if (INVALID_HANDLE_VALUE != hDeviceKey)
  589. {
  590. int nPort;
  591. size_t nLen;
  592. LPTSTR pszPortName;
  593.  
  594. nPort = 0;
  595. pszPortName = NULL;
  596.  
  597. {
  598. //First query for the size of the registry value
  599. DWORD dwType;
  600. DWORD dwDataSize;
  601. LONG err;
  602. DWORD dwAllocatedSize;
  603. DWORD dwReturnedSize;
  604. dwType = 0; dwDataSize = 0;
  605.  
  606. err = RegQueryValueEx(hDeviceKey, TEXT("PortName"), NULL,
  607. &dwType, NULL, &dwDataSize);
  608.  
  609. if (ERROR_SUCCESS != err)
  610. continue;
  611.  
  612. //Ensure the value is a string
  613. if (dwType != REG_SZ)
  614. continue;
  615.  
  616. //Allocate enough bytes for the return value
  617. dwAllocatedSize = dwDataSize + sizeof(TCHAR);
  618.  
  619. /* +sizeof(TCHAR) is to allow us to NULL terminate
  620. the data if it is not null terminated in the registry
  621. */
  622.  
  623. pszPortName = (LPTSTR)LocalAlloc(LMEM_FIXED, dwAllocatedSize);
  624.  
  625. if (pszPortName == NULL)
  626. continue;
  627.  
  628. //Recall RegQueryValueEx to return the data
  629. pszPortName[0] = TEXT('\0');
  630. dwReturnedSize = dwAllocatedSize;
  631.  
  632. err = RegQueryValueEx(hDeviceKey, TEXT("PortName"), NULL,
  633. &dwType, (LPBYTE)pszPortName, &dwReturnedSize);
  634.  
  635. if (ERROR_SUCCESS != err)
  636. {
  637. LocalFree(pszPortName);
  638. pszPortName = NULL;
  639. continue;
  640. }
  641.  
  642. //Handle the case where the data just returned is the same size as the allocated size. This could occur where the data
  643. //has been updated in the registry with a non null terminator between the two calls to ReqQueryValueEx above. Rather than
  644. //return a potentially non-null terminated block of data, just fail the method call
  645. if (dwReturnedSize >= dwAllocatedSize)
  646. continue;
  647.  
  648. //NULL terminate the data if it was not returned NULL terminated because it is not stored null terminated in the registry
  649. if (pszPortName[dwReturnedSize/sizeof(TCHAR) - 1] != _T('\0'))
  650. pszPortName[dwReturnedSize/sizeof(TCHAR)] = _T('\0');
  651. }/*local varable*/
  652.  
  653. //If it looks like "COMX" then
  654. //add it to the array which will be returned
  655. nLen = _tcslen(pszPortName);
  656.  
  657. if (3 < nLen)
  658. {
  659. if (0 == _tcsnicmp(pszPortName, TEXT("COM"), 3))
  660. {
  661. if(FALSE == isdigit(pszPortName[3]) )
  662. continue;
  663.  
  664. //Work out the port number
  665. _tcsncpy(pPortName + jj*strMaxLen, pszPortName,
  666. _tcsnlen(pszPortName, strMaxLen));
  667.  
  668. //_stprintf_s(&portName[jj][0], strMaxLen, TEXT("%s"), pszPortName);
  669. }
  670. else
  671. {
  672. continue;
  673. }/*if 0 == _tcsnicmp(pszPortName, TEXT("COM"), 3)*/
  674.  
  675. }/*if 3 < nLen*/
  676.  
  677. LocalFree(pszPortName);
  678. isFound = TRUE;
  679.  
  680. //Close the key now that we are finished with it
  681. RegCloseKey(hDeviceKey);
  682. }/*INVALID_HANDLE_VALUE != hDeviceKey*/
  683.  
  684. if(FALSE == isFound)
  685. continue;
  686.  
  687. //If the port was a serial port, then also try to get its friendly name
  688. {
  689. TCHAR szFriendlyName[1024];
  690. DWORD dwSize;
  691. DWORD dwType;
  692. szFriendlyName[0] = _T('\0');
  693. dwSize = sizeof(szFriendlyName);
  694. dwType = 0;
  695.  
  696. if( (TRUE == SetupDiGetDeviceRegistryPropertyFunPtr(hDevInfoSet, &devInfo,
  697. SPDRP_DEVICEDESC, &dwType, (PBYTE)(szFriendlyName),
  698. dwSize, &dwSize) ) && (REG_SZ == dwType)
  699. )
  700. {
  701. _tcsncpy(pFriendName + jj*strMaxLen, &szFriendlyName[0],
  702. _tcsnlen(&szFriendlyName[0], strMaxLen));
  703. }
  704. else
  705. {
  706. _stprintf_s(pFriendName + jj*strMaxLen, strMaxLen, TEXT(""));
  707. }/*if SetupDiGetDeviceRegistryPropertyFunPtr */
  708. }/*local variable */
  709.  
  710. jj++;
  711. }while(1);
  712.  
  713. HeapFree(GetProcessHeap(), 0, pGuid);
  714.  
  715. *pNumber = jj;
  716.  
  717. if(0 <jj)
  718. ret = TRUE;
  719.  
  720. return ret;
  721. }/*EnumerateComPortSetupAPISetupDiClassGuidsFromNamePort*/
  722.  
  723. BOOL EnumerateComPortRegistry(UINT *pNumber, TCHAR *pPortName, int strMaxLen)
  724. {
  725. //What will be the return value from this function (assume the worst)
  726. UINT jj;
  727. BOOL ret;
  728.  
  729. HKEY hSERIALCOMM;
  730. ret = FALSE;
  731.  
  732. if ( ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  733. TEXT("HARDWARE\\DEVICEMAP\\SERIALCOMM"), 0, KEY_QUERY_VALUE, &hSERIALCOMM)
  734. )
  735. {
  736. //Get the max value name and max value lengths
  737. DWORD dwMaxValueNameLen;
  738. DWORD dwMaxValueLen;
  739. DWORD dwQueryInfo;
  740.  
  741. dwQueryInfo = RegQueryInfoKey(hSERIALCOMM, NULL, NULL,
  742. NULL, NULL, NULL, NULL, NULL,
  743. &dwMaxValueNameLen, &dwMaxValueLen, NULL, NULL);
  744.  
  745. if(ERROR_SUCCESS == dwQueryInfo)
  746. {
  747. DWORD dwMaxValueNameSizeInChars, dwMaxValueNameSizeInBytes,
  748. dwMaxValueDataSizeInChars, dwMaxValueDataSizeInBytes;
  749.  
  750. DWORD *pValueName;
  751. DWORD *pValueData;
  752.  
  753. dwMaxValueNameSizeInChars = dwMaxValueNameLen + 1; //Include space for the NULL terminator
  754. dwMaxValueNameSizeInBytes = dwMaxValueNameSizeInChars * sizeof(TCHAR);
  755. dwMaxValueDataSizeInChars = dwMaxValueLen/sizeof(TCHAR) + 1; //Include space for the NULL terminator
  756. dwMaxValueDataSizeInBytes = dwMaxValueDataSizeInChars * sizeof(TCHAR);
  757.  
  758. //Allocate some space for the value name and value data
  759.  
  760. pValueName = (GUID*)HeapAlloc(GetProcessHeap(),
  761. HEAP_GENERATE_EXCEPTIONS| HEAP_ZERO_MEMORY, dwMaxValueNameSizeInBytes);
  762. pValueData = (GUID*)HeapAlloc(GetProcessHeap(),
  763. HEAP_GENERATE_EXCEPTIONS| HEAP_ZERO_MEMORY, dwMaxValueDataSizeInBytes);
  764.  
  765. if(NULL != pValueName && NULL != pValueData)
  766. {
  767. //Enumerate all the values underneath HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM
  768. DWORD i;
  769. DWORD dwType;
  770. DWORD dwValueNameSize;
  771. DWORD dwDataSize;
  772. LONG nEnum;
  773.  
  774. dwValueNameSize = dwMaxValueNameSizeInChars;
  775. dwDataSize = dwMaxValueDataSizeInBytes;
  776.  
  777. i = 0;
  778.  
  779. nEnum = RegEnumValue(hSERIALCOMM, i,
  780. pValueName, &dwValueNameSize, NULL, &dwType,
  781. pValueData, &dwDataSize);
  782.  
  783. jj = 0;
  784. while (ERROR_SUCCESS == nEnum)
  785. {
  786. //If the value is of the correct type, then add it to the array
  787. if (REG_SZ == dwType)
  788. {
  789. _stprintf_s(pPortName + jj*strMaxLen,
  790. strMaxLen, TEXT("%s"), pValueData);
  791. jj++;
  792. }/*if */
  793.  
  794. //Prepare for the next time around
  795. dwValueNameSize = dwMaxValueNameSizeInChars;
  796. dwDataSize = dwMaxValueDataSizeInBytes;
  797. ZeroMemory(pValueName, dwMaxValueNameSizeInBytes);
  798. ZeroMemory(pValueData, dwMaxValueDataSizeInBytes);
  799. i++;
  800. nEnum = RegEnumValue(hSERIALCOMM, i, pValueName,
  801. &dwValueNameSize, NULL, &dwType, pValueData, &dwDataSize);
  802. }/*while*/
  803. }
  804. else
  805. {
  806. return FALSE;
  807. }/*if NULL != pValueName && NULL != pValueData*/
  808.  
  809. HeapFree(GetProcessHeap(), 0, pValueName);
  810. HeapFree(GetProcessHeap(), 0, pValueData);
  811. }/*ERROR_SUCCESS == dwQueryInfo*/
  812.  
  813. //Close the registry key now that we are finished with it
  814. RegCloseKey(hSERIALCOMM);
  815.  
  816. if (dwQueryInfo != ERROR_SUCCESS)
  817. return FALSE;
  818. }/*ERROR_SUCCESS == RegOpenKeyEx*/
  819.  
  820. *pNumber = jj;
  821.  
  822. if(0 <jj)
  823. ret = TRUE;
  824.  
  825. return ret;
  826. }/*EnumerateComPortRegistry*/
  827.  
  828. #define TIMER_BEGIN(TIMER_LABEL) \
  829. { unsigned int tBegin##TIMER_LABEL, tEnd##TIMER_LABEL; \
  830. tBegin##TIMER_LABEL = GetTime();
  831.  
  832. #define TIMER_END(TIMER_LABEL) \
  833. tEnd##TIMER_LABEL = GetTime();\
  834. fprintf(stderr, "%s cost time = %d ms\n", \
  835. #TIMER_LABEL, tEnd##TIMER_LABEL - tBegin##TIMER_LABEL); \
  836. }
  837.  
  838. unsigned int GetTime(void)
  839. {
  840. /*winmm.lib*/
  841. return ( unsigned int)timeGetTime();
  842. }/*GetTime*/
  843.  
  844. int main(int argc, TCHAR argv[])
  845. {
  846. TCHAR portName[MAX_PORT_NUM][MAX_STR_LEN];
  847. TCHAR friendlyName[MAX_PORT_NUM][MAX_STR_LEN];
  848. UINT i;
  849. UINT n;
  850.  
  851. char* nativeLocale;
  852.  
  853. nativeLocale = _strdup( setlocale(LC_CTYPE,NULL) );
  854.  
  855. for(i = 0; i< MAX_PORT_NUM; i++)
  856. ZeroMemory(&portName[i][0], MAX_STR_LEN);
  857.  
  858. _tprintf(TEXT("\nCreateFile method : \n"));
  859. TIMER_BEGIN(EnumerateComPortByCreateFile);
  860. EnumerateComPortByCreateFile(&n, &portName[0][0], MAX_STR_LEN);
  861. TIMER_END(EnumerateComPortByCreateFile);
  862. _tprintf(TEXT("sought %d:\n"), n);
  863. for(i = 0; i< n; i++)
  864. _tprintf(TEXT("\t%s\n"), &portName[i][0]);
  865.  
  866. for(i = 0; i< MAX_PORT_NUM; i++)
  867. ZeroMemory(&portName[0][0], MAX_STR_LEN);
  868.  
  869. _tprintf(TEXT("\nQueryDosDevice method : "));
  870. TIMER_BEGIN(EnumerateComPortQueryDosDevice);
  871. EnumerateComPortQueryDosDevice(&n, &portName[0][0], MAX_STR_LEN);
  872. TIMER_END(EnumerateComPortQueryDosDevice);
  873. _tprintf(TEXT("sought %d:\n"), n);
  874.  
  875. for(i = 0; i< n; i++)
  876. _tprintf("\t%s\n", &portName[i][0]);
  877.  
  878. for(i = 0; i< MAX_PORT_NUM; i++)
  879. ZeroMemory(&portName[i][0], MAX_STR_LEN);
  880.  
  881. _tprintf(TEXT("\nGetDefaultCommConfig method : \n"));
  882. TIMER_BEGIN(EnumerateComPortByGetDefaultCommConfig);
  883. EnumerateComPortByGetDefaultCommConfig(&n, &portName[0][0], MAX_STR_LEN);
  884. TIMER_END(EnumerateComPortByGetDefaultCommConfig);
  885. _tprintf(TEXT("sought %d:\n"), n);
  886.  
  887. for(i = 0; i< n; i++)
  888. _tprintf(TEXT("\t%s\n"), &portName[i][0]);
  889.  
  890. for(i = 0; i< MAX_PORT_NUM; i++){
  891. ZeroMemory(&portName[i][0], MAX_STR_LEN);
  892. ZeroMemory(&friendlyName[i][0], MAX_STR_LEN);
  893. }/*for i[i MAX_PORT_NUM]*/
  894.  
  895. _tprintf(TEXT("\nSetupAPI GUID_DEVINTERFACE_COMPORT method : \n"));
  896.  
  897. TIMER_BEGIN(EnumerateComPortSetupAPI_GUID_DEVINTERFACE_COMPORT);
  898. EnumerateComPortSetupAPI_GUID_DEVINTERFACE_COMPORT(&n, &portName[0][0],
  899. MAX_STR_LEN, &friendlyName[0][0]);
  900. TIMER_END(EnumerateComPortSetupAPI_GUID_DEVINTERFACE_COMPORT);
  901.  
  902. _tprintf(TEXT("sought %d:\n"), n);
  903.  
  904. setlocale(LC_CTYPE, "" );
  905. for(i = 0; i< n; i++)
  906. _tprintf(TEXT("\t%s <%s> \n"), &portName[i][0], &friendlyName[i][0]);
  907. setlocale(LC_CTYPE, nativeLocale);
  908.  
  909. for(i = 0; i< MAX_PORT_NUM; i++){
  910. ZeroMemory(&portName[i][0], MAX_STR_LEN);
  911. ZeroMemory(&friendlyName[i][0], MAX_STR_LEN);
  912. }/*for i[i MAX_PORT_NUM]*/
  913.  
  914. _tprintf(TEXT("\nSetupAPI SetupDiClassGuidsFromNamePort method : \n"));
  915.  
  916. TIMER_BEGIN(EnumerateComPortSetupAPISetupDiClassGuidsFromNamePort);
  917. EnumerateComPortSetupAPISetupDiClassGuidsFromNamePort(&n, &portName[0][0],
  918. MAX_STR_LEN, &friendlyName[0][0]);
  919. TIMER_END(EnumerateComPortSetupAPISetupDiClassGuidsFromNamePort);
  920.  
  921. _tprintf(TEXT("sought %d:\n"), n);
  922.  
  923. setlocale(LC_CTYPE, "" );
  924. for(i = 0; i< n; i++)
  925. _tprintf(TEXT("\t%s <%s> \n"), &portName[i][0], &friendlyName[i][0]);
  926. setlocale(LC_CTYPE, nativeLocale);
  927.  
  928. for(i = 0; i< MAX_PORT_NUM; i++)
  929. ZeroMemory(&portName[i][0], MAX_STR_LEN);
  930.  
  931. _tprintf(TEXT("\nRegistry method : \n"));
  932.  
  933. TIMER_BEGIN(EnumerateComPortRegistry);
  934. EnumerateComPortRegistry(&n, &portName[0][0], MAX_STR_LEN);
  935. TIMER_END(EnumerateComPortRegistry);
  936. _tprintf(TEXT("sought %d:\n"), n);
  937. for(i = 0; i< n; i++)
  938. _tprintf(TEXT("\t%s\n"), &portName[i][0]);
  939.  
  940. return 0;
  941. }/*main*/

The output be :

  1. CreateFile method :
  2. EnumerateComPortByCreateFile cost time = 35 ms
  3. sought 5:
  4. \\.\COM1
  5. \\.\COM4
  6. \\.\COM5
  7. \\.\COM98
  8. \\.\COM99
  9.  
  10. QueryDosDevice method : EnumerateComPortQueryDosDevice cost time = 7 ms
  11. sought 5:
  12. COM5
  13. COM1
  14. COM98
  15. COM99
  16. COM4
  17.  
  18. GetDefaultCommConfig method :
  19. EnumerateComPortByGetDefaultCommConfig cost time = 772 ms
  20. sought 5:
  21. COM1
  22. COM4
  23. COM5
  24. COM98
  25. COM99
  26.  
  27. SetupAPI GUID_DEVINTERFACE_COMPORT method :
  28. EnumerateComPortSetupAPI_GUID_DEVINTERFACE_COMPORT cost time = 12 ms
  29. sought 2:
  30. COM1 <通訊連接埠>
  31. COM4 <USB Serial Port>
  32.  
  33. SetupAPI SetupDiClassGuidsFromNamePort method :
  34. EnumerateComPortSetupAPISetupDiClassGuidsFromNamePort cost time = 10 ms
  35. sought 5:
  36. COM99 <Bluetooth Serial Port>
  37. COM4 <USB Serial Port>
  38. COM1 <通訊連接埠>
  39. COM98 <Bluetooth Serial Port>
  40. COM5 <USB-SERIAL CH340>
  41.  
  42. Registry method :
  43. EnumerateComPortRegistry cost time = 0 ms
  44. sought 5:
  45. COM98
  46. COM99
  47. COM5
  48. COM1
  49. 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

  1. BOOL EnumerateComPortSetupAPISetupDiClassGuidsFromNamePort(UINT *pNumber,
  2. 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. B - Even Odds

    Problem description Being a nonconformist, Volodya is displeased with the current state of things, p ...

  2. webpack打包css自动添加css3前缀

    为了浏览器的兼容性,有时候我们必须加入-webkit,-ms,-o,-moz这些前缀.目的就是让我们写的页面在每个浏览器中都可以顺利运行. 1.安装 cnpm i postcss-loader aut ...

  3. 关于生sql中的空值

    在数据库中的空值无非就是两种形式的表示 一种是什么都没有,一种是以NuLL显示的 , 那么在C# 读取出来怎么判断呢, DtStatus.Rows[0]["FetchCode"]. ...

  4. MySQL 5.6 Reference Manual-14.1 Introduction to InnoDB

    14.1 Introduction to InnoDB 14.1.1 InnoDB as the Default MySQL Storage Engine 14.1.2 Checking InnoDB ...

  5. ML一些简单的资源

    参考文献及推荐阅读 维基百科,http://en.wikipedia.org/wiki/K-nearest_neighbor_algorithm: 机器学习中的相似性度量,http://www.cnb ...

  6. 路飞学城Python-Day59(第五模块思维导图)

  7. 关于TCP中对于ACK报文是否需要确认的理解

    首先,TCP是一个面向字节流的协议,它不会对自己的内容做出任何的解释,也不需要做出解释,具体的解释由上层的协议来处理. 其次,TCP是一个面向字节流的协议,它会对它发送的每一个字节负责,确保每一个字节 ...

  8. 使用awk提取字符串中的数字或字母

    1.提取字符串中的数字 $ echo 'dsFUs34tg*fs5a%8ar%$#@' |awk -F "" ' { for(i=1;i<=NF;i++) { if ($i ...

  9. pywinauto进阶练习

    case1.画图工具简单练习 #_*_coding=utf-8_*_ import time from pprint import pprint import logging from logging ...

  10. C语言基础 (10) 变量作用域,生命周期 内存结构

    01 课程回顾 1.指针数组 注意: 对于数组来说,在使用sizeof的时候a和&a[0]是不一样的, 虽然以%x打印出来他们都是地址 2.值传递 int a; fun(a); int *** ...