For me using setupapi is the most strait-forward method to retrieve a list of serial ports on Windows.
I should note, that this code works well on XP and higher.
Example of output:
I'm going to offer this code to guys of node-serialport, because current method doesn't work well on some setups with antivir.
Update: The patch is ready https://github.com/Zensey/node-serialport/commit/b4c8e830d248c4f7e3be0fd0d2cd56a18159f2d9
References:
1. https://social.msdn.microsoft.com/Forums/vstudio/en-US/2555943e-0c69-4357-a85b-d6540bcaaf84/using-setupapi-to-return-all-guiddevclassdisplay?forum=vcgeneral
2. http://stackoverflow.com/a/3439805
3. https://gist.github.com/Zensey/7bd7781a28d6be306be5cdd70539dc65
I should note, that this code works well on XP and higher.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "stdafx.h" | |
#include <stdio.h> | |
#include <string.h> | |
#include <windows.h> | |
#include <Setupapi.h> | |
#include <devguid.h> | |
#include "conio.h" | |
#include "tchar.h" | |
// Get-WMIObject Win32_pnpentity | ? Description -like "*CH*" | |
// Get-WMIObject Win32_pnpentity | ? Manufacturer -like "*chip*" | |
/* http://stackoverflow.com/a/3439805 */ | |
#include <cfgmgr32.h> // for MAX_DEVICE_ID_LEN, CM_Get_Parent and CM_Get_Device_ID | |
#define INITGUID | |
//#include "c:\WinDDK\7600.16385.1\inc\api\devpkey.h" | |
// include DEVPKEY_Device_BusReportedDeviceDesc from WinDDK\7600.16385.1\inc\api\devpropdef.h | |
#ifdef DEFINE_DEVPROPKEY | |
#undef DEFINE_DEVPROPKEY | |
#endif | |
#ifdef INITGUID | |
#define DEFINE_DEVPROPKEY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8, pid) EXTERN_C const DEVPROPKEY DECLSPEC_SELECTANY name = { { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }, pid } | |
#else | |
#define DEFINE_DEVPROPKEY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8, pid) EXTERN_C const DEVPROPKEY name | |
#endif // INITGUID | |
// include DEVPKEY_Device_BusReportedDeviceDesc from WinDDK\7600.16385.1\inc\api\devpkey.h | |
DEFINE_DEVPROPKEY(DEVPKEY_Device_BusReportedDeviceDesc, 0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2, 4); // DEVPROP_TYPE_STRING | |
DEFINE_DEVPROPKEY(DEVPKEY_Device_ContainerId, 0x8c7ed206, 0x3f8a, 0x4827, 0xb3, 0xab, 0xae, 0x9e, 0x1f, 0xae, 0xfc, 0x6c, 2); // DEVPROP_TYPE_GUID | |
DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); // DEVPROP_TYPE_STRING | |
DEFINE_DEVPROPKEY(DEVPKEY_DeviceDisplay_Category, 0x78c34fc8, 0x104a, 0x4aca, 0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57, 0x5a); // DEVPROP_TYPE_STRING_LIST | |
DEFINE_DEVPROPKEY(DEVPKEY_Device_LocationInfo, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 15); // DEVPROP_TYPE_STRING | |
DEFINE_DEVPROPKEY(DEVPKEY_Device_Manufacturer, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 13); // DEVPROP_TYPE_STRING | |
DEFINE_DEVPROPKEY(DEVPKEY_Device_SecuritySDS, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 26); // DEVPROP_TYPE_SECURITY_DESCRIPTOR_STRING | |
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) | |
#pragma comment (lib, "setupapi.lib") | |
typedef BOOL(WINAPI *FN_SetupDiGetDevicePropertyW)( | |
__in HDEVINFO DeviceInfoSet, | |
__in PSP_DEVINFO_DATA DeviceInfoData, | |
__in const DEVPROPKEY *PropertyKey, | |
__out DEVPROPTYPE *PropertyType, | |
__out_opt PBYTE PropertyBuffer, | |
__in DWORD PropertyBufferSize, | |
__out_opt PDWORD RequiredSize, | |
__in DWORD Flags | |
); | |
wchar_t *subString(wchar_t *someString, int n) | |
{ | |
wchar_t *new_ = (wchar_t*) malloc(sizeof(wchar_t)*n + 1); | |
_tcsncpy_s(new_, 5, someString, n); | |
new_[n] = '\0'; | |
return new_; | |
} | |
int main(int argc, char ** argv) | |
{ | |
static const GUID g1 = { 0x2C7089AA, 0x2E0E, 0x11D1, { 0xB1, 0x14, 0x00, 0xC0, 0x4F, 0xC2, 0xAA, 0xE4 } }; | |
static const GUID g2 = { 0x86E0D1E0, 0x8089, 0x11D0, { 0x9C, 0xE4, 0x08, 0x00, 0x3E, 0x30, 0x1F, 0x73 } }; | |
static const GUID g3 = { 0x4D36E978, 0xE325, 0x11CE, { 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 } }; | |
static const GUID g4 = { 0xCC0EF009, 0xB820, 0x42F4, { 0x95, 0xA9, 0x9B, 0xFA, 0x6A, 0x5A, 0xB7, 0xAB } }; | |
static const GUID g5 = { 0x9341CD95, 0x4371, 0x4A37, { 0xA5, 0xAF, 0xFD, 0xB0, 0xA9, 0xD1, 0x96, 0x31 } }; | |
GUID *guidDev = (GUID*)& GUID_DEVCLASS_PORTS; | |
HDEVINFO hDevInfo = SetupDiGetClassDevs(guidDev, NULL, NULL, DIGCF_PRESENT | DIGCF_PROFILE); /* DIGCF_DEVICEINTERFACE */ | |
int memberIndex = 0; | |
DWORD dwSize, dwPropertyRegDataType; | |
WCHAR szBuffer[400]; | |
DEVPROPTYPE ulPropertyType; | |
TCHAR *id = NULL; | |
TCHAR *vid = NULL; | |
TCHAR *pid = NULL; | |
TCHAR *name = NULL; | |
TCHAR *manuf = NULL; | |
TCHAR *description = NULL; | |
SP_DEVINFO_DATA deviceInfoData; | |
FN_SetupDiGetDevicePropertyW fn_SetupDiGetDevicePropertyW = (FN_SetupDiGetDevicePropertyW) GetProcAddress(GetModuleHandle(TEXT("Setupapi.dll")), "SetupDiGetDevicePropertyW"); | |
while (true) { | |
id = NULL; | |
vid = NULL; | |
pid = NULL; | |
name = NULL; | |
manuf = NULL; | |
description = NULL; | |
ZeroMemory(&deviceInfoData, sizeof(SP_DEVINFO_DATA)); | |
deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); | |
if (SetupDiEnumDeviceInfo(hDevInfo, memberIndex, &deviceInfoData) == FALSE) { | |
if (GetLastError() == ERROR_NO_MORE_ITEMS) { | |
break; | |
} | |
} | |
dwSize = sizeof(szBuffer); | |
SetupDiGetDeviceInstanceId(hDevInfo, &deviceInfoData, szBuffer, dwSize, &dwSize); | |
szBuffer[dwSize] = '\0'; | |
id = _wcsdup(szBuffer); | |
vid = wcsstr(szBuffer, _T("VID_")); | |
if (vid) { | |
vid += 4; | |
vid = subString(vid, 4); | |
} | |
pid = wcsstr(szBuffer, _T("PID_")); | |
if (pid) { | |
pid += 4; | |
pid = subString(pid, 4); | |
} | |
if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, &deviceInfoData, SPDRP_DEVICEDESC, &dwPropertyRegDataType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize)) { | |
description = _wcsdup(szBuffer); | |
} | |
if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, &deviceInfoData, SPDRP_MFG, &dwPropertyRegDataType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize)) { | |
manuf = _wcsdup(szBuffer); | |
} | |
TCHAR szHardwareIDs[4096]; | |
if (SetupDiGetDeviceRegistryProperty(hDevInfo, &deviceInfoData, SPDRP_HARDWAREID, | |
&dwPropertyRegDataType, (BYTE*)szHardwareIDs, | |
sizeof(szHardwareIDs), // The size, in bytes | |
&dwSize)) { | |
LPCTSTR pszId; | |
_tprintf(TEXT("Hardware IDs:\n")); | |
for (pszId = szHardwareIDs; | |
*pszId != TEXT('\0') && pszId + dwSize / sizeof(TCHAR) <= szHardwareIDs + ARRAYSIZE(szHardwareIDs); | |
pszId += lstrlen(pszId) + 1) { | |
_tprintf(TEXT(" -> \"%s\"\n"), pszId); | |
} | |
} | |
HKEY hkey = SetupDiOpenDevRegKey(hDevInfo, &deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); | |
if (hkey != INVALID_HANDLE_VALUE) { | |
dwSize = sizeof(szBuffer); | |
if (RegQueryValueEx(hkey, _T("PortName"), NULL, NULL, (LPBYTE)&szBuffer, &dwSize) == ERROR_SUCCESS) { | |
szBuffer[dwSize] = '\0'; | |
name = _wcsdup(szBuffer); | |
} | |
} | |
if (fn_SetupDiGetDevicePropertyW) { // Vista and higher | |
if (fn_SetupDiGetDevicePropertyW(hDevInfo, &deviceInfoData, &DEVPKEY_Device_Manufacturer, | |
&ulPropertyType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize, 0)) { | |
manuf = _wcsdup(szBuffer); | |
} | |
} else { // XP | |
} | |
_tprintf(TEXT("Id: %s \n"), id); | |
_tprintf(TEXT("Vid: %s \n"), vid); | |
_tprintf(TEXT("Pid: %s \n"), pid); | |
_tprintf(TEXT("Device Description: %s\n"), description); | |
_tprintf(TEXT("Manufacturer: %s \n"), manuf); | |
_tprintf(_T("PortName %s\n"), name); | |
free(id); | |
free(vid); | |
free(pid); | |
free(description); | |
free(manuf); | |
free(name); | |
RegCloseKey(hkey); | |
memberIndex++; | |
} | |
if (hDevInfo) { | |
SetupDiDestroyDeviceInfoList(hDevInfo); | |
} | |
_getch(); | |
return 0; | |
} |
Example of output:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Hardware IDs: | |
-> "USB\VID_10C4&PID_EA60&REV_0100" | |
-> "USB\VID_10C4&PID_EA60" | |
Id: USB\VID_10C4&PID_EA60\0001 | |
Vid: 10C4 | |
Pid: EA60 | |
Device Description: Silicon Labs CP210x USB to UART Bridge | |
Manufacturer: Silicon Laboratories | |
PortName COM18 | |
Hardware IDs: | |
-> "MF\PCI9710_COM" | |
Id: MF\PCI#VEN_9710&DEV_9835&SUBSYS_00121000&REV_01\6&20BEF77D&0&0800E5#CHILD0000 | |
Vid: (null) | |
Pid: (null) | |
Device Description: PCI Serial Port | |
Manufacturer: MosChip Semiconductor Technology Ltd | |
PortName COM6 |
Update: The patch is ready https://github.com/Zensey/node-serialport/commit/b4c8e830d248c4f7e3be0fd0d2cd56a18159f2d9
References:
1. https://social.msdn.microsoft.com/Forums/vstudio/en-US/2555943e-0c69-4357-a85b-d6540bcaaf84/using-setupapi-to-return-all-guiddevclassdisplay?forum=vcgeneral
2. http://stackoverflow.com/a/3439805
3. https://gist.github.com/Zensey/7bd7781a28d6be306be5cdd70539dc65