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