1 #if defined(_WIN32)
2
3 /*
4 * Copyright (c) 2014 Craig Lilley <cralilley@gmail.com>
5 * This software is made available under the terms of the MIT licence.
6 * A copy of the licence can be obtained from:
7 * http://opensource.org/licenses/MIT
8 */
9
10 #include "serial/serial.h"
11 #include <tchar.h>
12 #include <windows.h>
13 #include <setupapi.h>
14 #include <initguid.h>
15 #include <devguid.h>
16 #include <cstring>
17
18 using serial::PortInfo;
19 using std::string;
20 using std::vector;
21
22 static const DWORD port_name_max_length = 256;
23 static const DWORD friendly_name_max_length = 256;
24 static const DWORD hardware_id_max_length = 256;
25
26 // Convert a wide Unicode string to an UTF8 string
utf8_encode(const std::wstring & wstr)27 std::string utf8_encode(const std::wstring &wstr)
28 {
29 int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
30 std::string strTo(size_needed, 0);
31 WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
32 return strTo;
33 }
34
35 vector<PortInfo>
list_ports()36 serial::list_ports()
37 {
38 vector<PortInfo> devices_found;
39
40 HDEVINFO device_info_set = SetupDiGetClassDevs(
41 (const GUID *)&GUID_DEVCLASS_PORTS,
42 NULL,
43 NULL,
44 DIGCF_PRESENT);
45
46 unsigned int device_info_set_index = 0;
47 SP_DEVINFO_DATA device_info_data;
48
49 device_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
50
51 while (SetupDiEnumDeviceInfo(device_info_set, device_info_set_index, &device_info_data))
52 {
53 device_info_set_index++;
54
55 // Get port name
56
57 HKEY hkey = SetupDiOpenDevRegKey(
58 device_info_set,
59 &device_info_data,
60 DICS_FLAG_GLOBAL,
61 0,
62 DIREG_DEV,
63 KEY_READ);
64
65 TCHAR port_name[port_name_max_length];
66 DWORD port_name_length = port_name_max_length;
67
68 LONG return_code = RegQueryValueEx(
69 hkey,
70 _T("PortName"),
71 NULL,
72 NULL,
73 (LPBYTE)port_name,
74 &port_name_length);
75
76 RegCloseKey(hkey);
77
78 if (return_code != EXIT_SUCCESS)
79 continue;
80
81 if (port_name_length > 0 && port_name_length <= port_name_max_length)
82 port_name[port_name_length - 1] = '\0';
83 else
84 port_name[0] = '\0';
85
86 // Ignore parallel ports
87
88 if (_tcsstr(port_name, _T("LPT")) != NULL)
89 continue;
90
91 // Get port friendly name
92
93 TCHAR friendly_name[friendly_name_max_length];
94 DWORD friendly_name_actual_length = 0;
95
96 BOOL got_friendly_name = SetupDiGetDeviceRegistryProperty(
97 device_info_set,
98 &device_info_data,
99 SPDRP_FRIENDLYNAME,
100 NULL,
101 (PBYTE)friendly_name,
102 friendly_name_max_length,
103 &friendly_name_actual_length);
104
105 if (got_friendly_name == TRUE && friendly_name_actual_length > 0)
106 friendly_name[friendly_name_actual_length - 1] = '\0';
107 else
108 friendly_name[0] = '\0';
109
110 // Get hardware ID
111
112 TCHAR hardware_id[hardware_id_max_length];
113 DWORD hardware_id_actual_length = 0;
114
115 BOOL got_hardware_id = SetupDiGetDeviceRegistryProperty(
116 device_info_set,
117 &device_info_data,
118 SPDRP_HARDWAREID,
119 NULL,
120 (PBYTE)hardware_id,
121 hardware_id_max_length,
122 &hardware_id_actual_length);
123
124 if (got_hardware_id == TRUE && hardware_id_actual_length > 0)
125 hardware_id[hardware_id_actual_length - 1] = '\0';
126 else
127 hardware_id[0] = '\0';
128
129 #ifdef UNICODE
130 std::string portName = utf8_encode(port_name);
131 std::string friendlyName = utf8_encode(friendly_name);
132 std::string hardwareId = utf8_encode(hardware_id);
133 #else
134 std::string portName = port_name;
135 std::string friendlyName = friendly_name;
136 std::string hardwareId = hardware_id;
137 #endif
138
139 PortInfo port_entry;
140 port_entry.port = portName;
141 port_entry.description = friendlyName;
142 port_entry.hardware_id = hardwareId;
143
144 devices_found.push_back(port_entry);
145 }
146
147 SetupDiDestroyDeviceInfoList(device_info_set);
148
149 return devices_found;
150 }
151
152 #endif // #if defined(_WIN32)
153